C语言基本笔记(9)—— 数组和指针
以下是C语言的C99标准中有关数组和指针的一些理论。
数组
变长数组(VLA,Variable-Length Array)
- 定义:允许数组长度在运行时确定(需编译器支持)。
- 示例:
int n = 10; int arr[n]; // 合法(C99支持)
- 限制:
- 不能初始化(如
int arr[n] = {0};
非法)。 - 作用域内长度不可变。
- C11中变为可选特性,需注意编译器和平台兼容性。
- 不能初始化(如
数组类型退化(Decay)
- 数组名在大多数表达式中退化为指向首元素的指针(例外:
sizeof(arr)
、&arr
)。 - 示例:
int arr[3] = {1, 2, 3}; int *p = arr; // 等价于 int *p = &arr[0]; size_t size = sizeof(arr); // 返回数组总字节数(非指针大小)
指针的核心特性(C99标准)
-
restrict
关键字- 用途:声明指针是访问数据的唯一方式,帮助编译器优化。
- 示例:
void copy(int *restrict dest, const int *restrict src, int n);
-
指针算术与数组遍历
- 指针算术规则:
int arr[5]; int *p = arr; p++; // 指向arr[1](地址增加sizeof(int))
- 遍历优化:
for (int *p = arr; p < arr + 5; p++) { printf("%d ", *p); }
- 指针算术规则:
-
动态多维数组
- 堆内存分配:
int rows = 3, cols = 4; int **matrix = malloc(rows * sizeof(int*)); for (int i = 0; i < rows; i++) { matrix[i] = malloc(cols * sizeof(int)); }
- 连续内存分配(推荐):
int (*matrix)[cols] = malloc(rows * sizeof(*matrix)); // C99支持
- 堆内存分配:
数组与指针的关系
-
数组名与指针的区别
特性 数组名 指针 内存分配 静态分配(栈或全局区) 动态或静态(指向其他内存) sizeof
结果数组总字节数 指针变量大小(如4/8字节) 可修改性 不可重新赋值 可指向其他地址 -
函数参数传递
- 数组作为函数参数时退化为指针:
void func(int arr[]); // 等价于 void func(int *arr);
- 传递多维数组需明确列数(C99支持变长):
void func(int rows, int cols, int mat[rows][cols]);
- 数组作为函数参数时退化为指针:
字符数组与字符串的关系
1. 字符数组的定义与初始化
- 字符数组存储字符串
注意事项:char str1[] = "Hello"; // 自动包含'\0',大小为6 char str2[6] = "Hello"; // 等价于str1 char str3[5] = "Hello"; // 错误!缺少空间存放'\0' char str4[5] = {'H', 'e', 'l', 'l', 'o'}; // 合法,但非字符串(无'\0')
- 若字符数组用于存储字符串,必须预留空间给结尾的
'\0'
。 - 未显式初始化的字符数组内容不确定,需手动添加
'\0'
才能作为字符串使用。
- 若字符数组用于存储字符串,必须预留空间给结尾的
2. 字符串字面量初始化
- 隐式添加
'\0'
char str[] = "abc"; // 等价于 {'a', 'b', 'c', '\0'}
- 若数组大小不足,会静默截断且可能不添加
'\0'
:char str[3] = "abc"; // 错误!未包含'\0',可能导致越界
- 若数组大小不足,会静默截断且可能不添加
3. 字符数组与字符串的操作
-
字符串函数依赖
'\0'
char str[5] = "Hello"; // 错误!越界(实际需要6字节) printf("%s", str); // 可能导致未定义行为
正确做法:
char str[6] = "Hello"; // 正确,包含'\0' printf("%s", str); // 安全
-
手动添加
'\0'
char str[5] = {'H', 'e', 'l', 'l', 'o'}; str[4] = '\0'; // 手动终止,但数组长度不足,仍会越界!
常见错误
1. 数组越界访问
int arr[3] = {1, 2, 3};
printf("%d", arr[3]); // 越界访问,未定义行为
- 解决方法:始终检查索引范围(
0 <= index < size
)。
2. 混淆数组与指针
int arr[3] = {1, 2, 3};
int *p = arr;
printf("%zu", sizeof(arr)); // 输出12(假设int为4字节)
printf("%zu", sizeof(p)); // 输出指针大小(如4或8)
3. 字符串初始化陷阱
char str[5];
strcpy(str, "Hello"); // 错误!目标缓冲区不足(需要6字节)
4. 变长数组(VLA)的局限性
- VLA不能用于全局变量或静态变量:
int n = 10; static int vla[n]; // 错误!VLA不能是静态的
5. 多维数组的内存布局
- 多维数组是连续存储的,但动态分配时需注意内存连续性:
int (*matrix)[cols] = malloc(rows * sizeof(*matrix)); // 连续内存(C99)