C 语言 数组、字符串、函数
数组初始化的几种方式
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr1[5] = {1,2,3,4,5}; //数组的定义需要{},只有在定义的时候才能初始化
int arr2[5];
//arr2 = {1,2,3,4,5}; //err 这个是错误的
//数组全部元素初始化为 某个值
int arr3[5] = {0};
//如果定义的同时 初始化,第一个[]内可以不写内容
int arr4[] = {1,2,3,4,5}; //编译器会根据用户初始化的元素来确定数组的大小
//int arr4[]; //err //必须初始化的时候才能省略
int arr5[][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //第一个可以省略
return 0;
}
数组的名字代表着数组的首地址,以及使用 sizeof
来求数组长度的方法;
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("------------一维数组------------\n");
int arr[10];
//1. 数组名是数组首元素地址
printf("arr = %p, &arr[0] = %p\n", arr, &arr[0]); //两个是一样的
//2. 测试数组总的字节大小 : sizeof(数组名) 10 * 4 = 40
printf("sizeof(arr) = %u\n",sizeof(arr));
//3. 得到数组长度 数组总大小/每个元素大小 (常用)
int len = sizeof(arr)/sizeof(arr[0]);
printf("len(arr) = %d\n",len);
printf("------------二维数组------------\n");
int arr2[5][10];
// 1. 数组名是常量,不能修改
// a = 10; //err
//2. sizeof(数组名),测数组的总大小 5*int[10] = 5*4*10
printf("sizeof(arr2) = %u\n",sizeof(arr2));
//3. 求行数
int n = sizeof(arr2)/sizeof(arr2[0]);
printf("行数 = %d\n",n);
//4. 求列数
int m = sizeof(arr2[0])/sizeof(arr2[0][0]);
printf("列数 = %d\n",m);
//5. 总个数
printf("总个数 = %d\n",sizeof(arr2)/sizeof(arr2[0][0]));
return 0;
}
输出:
------------一维数组------------
arr = 0x7ffc86eac190, &arr[0] = 0x7ffc86eac190
sizeof(arr) = 40
len(arr) = 10
------------二维数组------------
sizeof(arr2) = 200
行数 = 5
列数 = 10
总个数 = 50
可以看出 arr
和 arr[0]
的地址是一样的。
字符数组与字符串
- C 语言中没有字符串这种数据类型,可以通过
char
的数组来替代; - 字符串一定是一个
char
的数组,但char
的数组未必是字符串; - 数字
0
(和字符‘\0’
等价) 结尾的char
数组就是一个字符串,但如果char
数组没有以数字0
结尾,那么就不是一个字符串,只是普通字符数组,所以字符串是一种特殊的char
的数组。
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 1. c 语言没有字符串类型,用字符数组模拟
char str[10];
// 2. 字符串一定是字符数组,字符数组不一定是字符串
// 3. 如果字符数组以'\0'(0) 结尾,就是字符串
char str2[] = {'a', 'b', 'c'};//字符数组 --> 注意这里[]内没有指定数字
char str3[10] = {'a', 'b', 'c', '\0'}; //字符串
char str4[10] = {'a', 'b', 'c', 0}; //字符串
return 0;
}
字符串输出乱码问题以及一系列字符串要注意的问题
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 1. 字符数组打印乱码问题
char a1[] = {'a', 'b', 'c'}; //字符数组
printf("a1 = %s\n",a1); //这个是乱码,因为字符数组后面没有'\0'
// 2. 正确的字符串
char a2[] = {'a', 'b', 'c', '\0'};
//char a2[] = {'a', 'b', 'c', 0}; // 0 也可以
// 3. 遇到'\0'提前截断
char a3[] = {'a', 'b', 'c', '\0', 'h', 'e', '\0'};
printf("a3 = %s\n", a3);//"abc"
// 4. 前 3 个字符赋值为 a, b, c, 后面自动赋值为 0
char a4[10] = {'a', 'b', 'c'};
printf("a4 = %s\n", a4);
// 5. 常用的初始化方法 --> 使用字符串初始化,在字符串结尾自动加结束符数字 0
// 这个结束符,用户看不到(隐藏),但是是存在的
char a5[10] = "abc";
printf("a5 = %s\n", a5);
// 6. 使用 sizeof() 测试隐藏的那个'\0'
char a6[] = "abc";
printf("sizeof(a6) = %d\n", sizeof(a6));
// 7. \0 后面最好别跟数字,不然有可能变成转义字符 例如\012 就是'\n'
char a7[] = "\012abc";
printf("a7 = %s\n", a7);
// 8. 最多写 9 个字符,留一个位置放结束符
char a8[10] = "123456789";
// 9. sizeof() 测数据类型大小,不会因为结束符提前结束 sizeof("123\045") = 5
char a9[] = "abc\0de";
printf("%u\n", sizeof(a9)); // 7 --> a b c \0 d e \0 不要忘记最后还有一个\0
// 10. scanf("%s", a); //a 没有&,原因数组名是首元素地址,本来就是地址
return 0;
}
输出结果:
随机数生成
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char const *argv[])
{
// 1. 先设置种子,种子设置一次即可
// 2. 如果 srand() 参数一样,随机数就一样
//srand(100);
// 由于希望产生的随机数不同,所以要使用系统时间作为随机数
srand( (unsigned int)time(NULL) ); // 返回的是 time_t 类型 相当于 long,单位为毫秒,注意强制转换一下
for(int i = 0; i < 3; i++){
printf("the rand number is = %d\n", rand());
}
return 0;
}
字符串相关函数
输入函数
① gets(str)
与 scanf("%s", str)
的区别:
gets(str)
允许输入的字符串含有空格;scanf(“%s”,str)
不允许含有空格;
注意:由于
scanf()
和gets()
无法知道字符串 s 大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出) 的情况。
② fgets 函数
相关:
char *fgets(char *s, int size, FILE *stream);
功能:从 stream 指定的文件内读入字符,保存到 s 所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了 size - 1 个字符为止,最后会自动加上字符 '\0' 作为字符串结束;
参数 :
s:字符串;
size:指定最大读取字符串的长度(size - 1)(大于这个长度就舍弃);
stream:文件指针,如果读键盘输入的字符串,固定写为 stdin;返回值:
成功:成功读取的字符串;
读到文件尾或出错: NULL;注意,
fgets()
在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车('\n') 也做为字符串的一部分。通过 scanf 和 gets 输入一个字符串的时候,不包含结尾的“\n”,但通过 fgets 结尾多了“\n”。fgets() 函数是安全的,不存在缓冲区溢出的问题。
简单测试:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 1. scanf() 输入字符串 不做越界检查,此函数不安全
// 2. gets(已抛弃,不安全)
char buf[10];
// gets(buf); //不安全 已废弃
// printf("buf = %s\n", buf);
// 3. fgets(安全,可以指定大小)
fgets(buf,sizeof(buf),stdin);
printf("buf = '%s'\n", buf);
// 4. 测试读入了 最后的换行
fgets(buf,sizeof(buf),stdin); // 注意虽然这里从键盘读入,但是如果缓冲区还有内容就不会读这里的
printf("buf2 = '%s'\n", buf);
return 0;
}
输入输出结果:
输出函数
int puts(const char *s);
功能:标准设备输出 s 字符串,在输出完成后自动输出一个'\n'。
int fputs(const char * str, FILE * stream); //文件操作
功能: 将 str 所指定的字符串写入到 stream 指定的文件中, 字符串结束符 '\0' 不写入文件。
简单测试:
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[] = "hello world!";
puts(str); //会自动加上一个 '\n'
fputs(str,stdout); // 不会自动加上 '\n'
return 0;
}
输出:
sizeof() 和 strlen() 的区别
注意:
strlen()
从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束);- 而
sizeof()
则不管遇不遇到'\0'
都会计算整个数据类型大小;
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[] = "hello";
// strlen 从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束)
int len = strlen(buf);
printf("strlen(buf) = %d\n", len); //5
printf("sizeof(buf) = %d\n", sizeof(buf)); // 6 这个还包括 '\0'
char buf2[] = "\0hello";
printf("strlen(buf2) = %d\n", strlen(buf2)); // 0
printf("sizeof(buf2) = %d\n", sizeof(buf2)); // 7 注意不要忘记最后还有一个 '\0'
char buf3[100] = "zxzxin";
printf("strlen(buf3) = %d\n", strlen(buf3)); //6
printf("sizeof(buf3) = %d\n", sizeof(buf3)); //100
return 0;
}
字符串拷贝 strcpy() 和 strncpy()
注意两者区别:
char *strcpy(char *dest, const char *src)
:把 src 所指向的字符串复制到 dest 所指向的空间中,'\0'也会拷贝过去。(如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出的错误情况。)char *strncpy(char *dest, const char *src, size_t n)
:把 src 指向字符串的前 n 个字符复制到 dest 所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
//strcpy: 把 src 所指向的字符串复制到 dest 所指向的空间中,'\0'也会拷贝过去
char src[] = "hello world!";
char dest[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
strcpy(dest,src);
printf("dest = %s\n", dest);// hello world! 不会输出后面的 aaaa, 因为'\0'也拷贝在后面了
// strncpy : 把 src 指向字符串的前 n 个字符复制到 dest 所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
char dest2[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
strncpy(dest2, src, strlen(src));
printf("dest2 = %s\n", dest2); //hello world!aaaaaaaaaaa
//但是如果拷贝的长度大于 strlen(src)
char dest3[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
strncpy(dest3, src, strlen(src)+1);
printf("dest3 = %s\n", dest3); //hello world!
return 0;
}
输出:
strcat()、strncat()、 strcmp()、strncmp()
char *strcat(char *dest, const char *src);
: 将 src 字符串连接到 dest 的尾部,‘\0’
也会追加过去;char *strncat(char *dest, const char *src, size_t n);
: 将 src 字符串前 n 个字符连接到 dest 的尾部,‘\0’
也会追加过去;int strcmp(const char *s1, const char *s2);
: 比较 s1 和 s2 的大小,比较的是字符 ASCII 码大小;int strncmp(const char *s1, const char *s2, size_t n);
:比较 s1 和 s2 前 n 个字符的大小,比较的是字符 ASCII 码大小;
测试:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char s1[] = "abc";
char s2[] = "abcd";
int flag = strcmp(s1, s2);
printf("flag = %d\n",flag);// <0
char s3[] = "abc";
char s4[] = "Abcd";
int flag2 = strncmp(s3, s4, 3); //指定比较前 3 个字符
printf("flag2 = %d\n",flag2);// >0s
printf("-------------strcat 和 strncat------------\n");
char src[] = " hello mike";
char dst[100] = "abc";
//把 src 的内容追加到 dst 的后面
//strcat(dst, src); //dst = "abc hello mike"
strncat(dst, src, strlen(" hello")); //指定长度追加 dst = "abc hello"
printf("dst = %s\n", dst);
return 0;
}
输出:
flag = -100
flag2 = 32
-------------strcat 和 strncat------------
dst = abc hello
sprintf()、sscanf()
int sprintf(char *str, const char *format, ...);
: 根据参数 format 字符串来转换并格式化数据,然后将结果输出到 str 指定的空间中,直到出现字符串结束符 '\0' 为止。int sscanf(const char *str, const char *format, ...);
: 从 str 指定的字符串读取数据,并根据参数 format 字符串来转换并格式化数据。
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("--------------sprintf-------------------\n");
int n = 10;
char ch = 'a';
char buf[10] = "hello";
char dest[30];
sprintf(dest,"n = %d, ch = %c, buf = %s\n", n, ch, buf);
printf("dest: %s", dest); // 注意这里没有加上 '\n' 但是之前里面有
printf("--------------sscanf-------------------\n");
// sscanf 和 spirnf 相反 这里从 dest 中读取
int n2;
char ch2;
char buf2[10];
sscanf(dest, "n = %d, ch = %c, buf = %s\n", &n2, &ch2, &buf2); //记得加上 &
printf("n2 = %d\n", n2);
printf("ch2 = %c\n", ch2);
printf("buf2 = %s\n", buf2);
printf("-----------字符串提取注意的地方--------------\n");
// 从字符串中提取 内容最好按照空格进行分割 ,不然有可能提取不出来
// 1. 按照空格分割 --> 正确
char buf3[] = "aaa bbb ccc";
char a[10],b[10],c[10];
sscanf(buf3, "%s %s %s", a,b,c); //注意没有&
printf("a = %s, b = %s, c = %s\n", a, b, c);
// 2. 按照逗号分割 --> 错误
char buf4[] = "aaa,bbb,ccc";
char a2[10],b2[10],c2[10];
sscanf(buf4, "%s,%s,%s", a2,b2,c2); //注意没有&
printf("a2 = %s, b2 = %s, c2 = %s\n", a2, b2, c2);
return 0;
}
结果:
strchr()、strstr()、strtok()
char *strchr(const char *s, char c);
: 在字符串 s 中查找字母 c 出现的位置;char *strstr(const char *haystack, const char *needle);
: 在字符串 haystack 中查找字符串 needle 出现的位置;char *strtok(char *str, const char *delim);
①来将字符串分割成一个个片段。当 strtok() 在参数 s 的字符串中发现参数 delim 中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0;
②在第一次调用时:strtok() 必需给予参数 s 字符串;
③往后的调用则将参数 s 设置成 NULL,每次调用成功则返回指向被分割出片段的指针;
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
printf("-------------strchr------------\n");
char str1[] = "aaabbbccc";
char *p = strchr(str1, 'b');
printf("p = %s\n", p);
printf("-------------strstr------------\n");
char str2[] = "ddddabcd123abcd333abcd";
char *p2 = strstr(str2, "abcd");
printf("p2 = %s\n", p2);
printf("-------------strtok------------\n");
char str3[100] = "adc*fvcv*ebcy*hghbdfg*casdert";
char *s = strtok(str3, "*"); //将"*"分割的子串取出
while (s != NULL){
printf("%s\n", s);
s = strtok(NULL, "*");//往后的调用则将参数 s 设置成 NULL,每次调用成功则返回指向被分割出片段的指针
}
return 0;
}
输出:
-------------strchr------------
p = bbbccc
-------------strstr------------
p2 = abcd123abcd333abcd
-------------strtok------------
adc
fvcv
ebcy
hghbdfg
casdert
函数
- 函数内部,包括() 内部的形参变量,只有在调用时分配空间,调用完毕自动释放;
return
和exit()
函数区别,只要一调用exit()
函数(不管在什么地方),整个程序就结束,但是只有在main
函数中调用return
才会结束程序;- 声明函数加不加
extern
关键字都一样, 声明函数可以不指定形参名称,只指定形参形参类型,但是定义不可以。 - 头文件一般是放函数声明;
看下面两张图解释 .h
文件的作用:
解决办法:
多个文件中(同一项目),不能出现同名函数(static 除外)。这就是为什么
.h
文件只放函数的声明,不放函数的定义;防止头文件重复包含: 当一个项目比较大时,往往都是分文件,这时候有可能不小心把同一个头文件
include
多次,或者头文件嵌套包含。
防止办法:
① #ifndef
方式;
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
// 声明语句
#endif
② #pragma once
方式。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: C 语言 数据类型、运算符等基础
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论