数据结构
数组
数组的声明格式很简单:type name[size(可选)]
提示
必须在编译期间指定大小,无法在运行期间指定
int arr[3];
double arr[];
数组通过下标来访问每个元素,从0
开始
数组的初始化是在编译阶段进行的
- 完全初始化 - 即数组多长就给多少个初始化值
- 部分初始化 - 只给了少于数组长度的初始化值,没有被初始化的部分会被赋值为
0
- 没有初始化
int arr1[3] = {1, 2, 6};
int arr2[3] = {3};
int arr3[3];
数组可以指定初始化某个位置的值,其余未初始化的会被初始化为0
。如果后面还有更多的值,这些会用于初始化指定元素后面的元素
int arr1[3] = {[2] = 22};
printf("%d", arr1[2]); // 22
int arr2[5] = {[2] = 3, 4, 5};
printf("%d", arr2[2]); // 3
printf("%d", arr2[3]); // 4
printf("%d", arr2[4]); // 5
如果没有指定长度,那么数组就会设置成足够装得下初始化的值
int arr[] = {[5] = 6};
printf("%d", arr[5]); // 6
计算大小
sizeof
可以返回一个数组占据的内存大小,但是每一个元素类型大小是相同的,利用这一点可以计算出元素的个数
int arr[] = {1, 2, 3};
printf("%d", sizeof(arr)); // 12
printf("%d", sizeof(arr) / sizeof(int)); // 3
但是有一种情况例外,数组作为函数参数时sizeof
无法准确计算数组大小。这是因为函数调用时会形成一个调用栈,数字可以很大,每次拷贝整个数组会耗费大量的内存,所以编译器看到数组作为函数参数时,不会拷贝整个数组,而是拷贝数组第一个元素的首地址
void foo(int arr[])
{
printf("%d", sizeof(arr)); // ?
}
int main(void)
{
int arr[] = {1, 2, 3};
foo(arr);
printf("%d", sizeof(arr)); // 12
return 0;
}
这里有一个小技巧,就是在计算元素的大小时使用sizeof(arr[0])
,而不是直接指定数组类型
int arr[] = {1, 2, 3};
printf("%d", sizeof(arr) / sizeof(arr[0]));
多维数组
数组中的每一个元素也可以是一个数组,因此就有了多维数组的存在,最常用的是用二维数组来表示矩阵
int arr[3][2];
提示
在二维数组中可以不用写行数,但列数必须写
边界问题
C 不会检查数组的越界访问,索引值超出了数组定义的大小并不会报错。这么做的原因是,C 相信人不会做出越界的傻事,由于它不需要进行边界检查,数组运行的更快
字符串
在讨论字符串时,基本上就是在讨论字符数组,字符串就是一组字符组成的。C 没有提供专门存储字符串的变量类型,字符串被存储在char
类型的数组中,每个单元存储一个字符串中的字符,并且在末尾都有一个\0
表示字符串的结束,这意味着数组的容量至少比字符串的字符数量多一个。这个可以用string.h
提供的strlen()
来验证,它只会计算字符个数,而不会计算不可见的末尾字符,但是sizeof
会计算所有的占用字节数,所以通常更大
// 使用双引号会自动在末尾补充 \0
char str[] = "hello";
printf("%d\n", sizeof(str)); // 6
printf("%d\n", strlen(str)); // 5
至于为什么需要更多容量来存储字符串,则是因为需要一个符号来标记一个字符串的末尾,通常使用 NUL 字符来表示,而它的 ASCII 值为 0,所以最后一个位置就是\0
。C 中的所有字符串操作函数,都期望字符串是以\0
结束的,这是一个规则,因此字符串必须以 NUL 结束,这就是为什么需要一个额外的空间的原因
char str[3];
str[0] = '1'
str[1] = '2'
str[2] = '3'
// printf 无法识别这个字符数组是一个字符串,可能会打印垃圾值
printf("%s", str); // 123?
因此,要这样定义:
char str[] = {'1', '2', '3', '\0'};
虽然使用printf
输出一个字符串使用%s
,但更常用的是使用puts
函数和gets
函数来输出和输入字符串,puts
只会显示字符串,且在末尾加上换行符
字符串函数
int strlen(str)
:计算字符个数strcat(str1, str2)
:拼接字符串strcmp(str1, str2)
:比较字符串strcpy(char*, char*)
:拷贝字符串