C语言数组详解:一维数组、二维数组与内存布局

C语言数组详解:一维数组、二维数组与内存布局

1. 数组是什么

数组是一组类型相同、在内存中连续存放的数据。数组中的每个元素通过下标访问,下标从 0 开始。

1
int scores[5] = {90, 85, 76, 93, 88};

数组共有 5 个 int 元素,有效下标为 0~4。

2. 一维数组

2.1 创建与初始化

1
2
3
4
5
int a[5];                       // 未初始化,局部数组内容不确定
int b[5] = {1, 2, 3, 4, 5}; // 完全初始化
int c[5] = {1, 2}; // 其余元素初始化为0
int d[5] = {0}; // 全部初始化为0
int e[] = {1, 2, 3}; // 编译器推导长度为3

数组长度必须足以容纳初始化数据,不能访问超出范围的元素。

2.2 遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
int arr[] = {1, 2, 3, 4, 5};
int count = (int)(sizeof(arr) / sizeof(arr[0]));

for (int i = 0; i < count; ++i)
printf("arr[%d] = %d\n", i, arr[i]);

return 0;
}

sizeof(arr) 是整个数组所占字节数,sizeof(arr[0]) 是一个元素的字节数。

2.3 连续存储

1
2
3
int arr[3] = {10, 20, 30};
for (int i = 0; i < 3; ++i)
printf("%p\n", (void*)&arr[i]);

相邻地址之差通常为 sizeof(int)。连续存储使按下标访问的时间复杂度为 O(1)。

3. 二维数组

二维数组可以理解为“由多个一维数组组成的数组”。

1
2
3
4
5
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};

3.1 常见初始化方式

1
2
3
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int b[2][3] = {1, 2, 3, 4};
int c[][3] = {{1, 2, 3}, {4, 5, 6}}; // 行数可省略,列数不能省略

3.2 遍历二维数组

1
2
3
4
5
6
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 4; ++j)
printf("%3d", matrix[i][j]);
printf("\n");
}

C语言中的二维数组按照“行优先”连续存储:先保存第0行,再保存第1行。

4. C99变长数组

C99允许数组长度在运行时确定:

1
2
3
4
5
void print_array(int n, int arr[n])
{
for (int i = 0; i < n; ++i)
printf("%d ", arr[i]);
}

变长数组并非所有编译器都完整支持。例如部分 Visual Studio C 编译环境不支持,应根据编译器选择使用。

5. 数组练习:二分查找

二分查找要求数组有序,每次排除一半区间,时间复杂度为 O(log n)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int binary_search(const int arr[], int size, int target)
{
int left = 0;
int right = size - 1;

while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] < target)
left = mid + 1;
else if (arr[mid] > target)
right = mid - 1;
else
return mid;
}
return -1;
}

6. 易错点

  • 数组下标越界属于未定义行为;
  • 局部数组未初始化时不能假设元素为0;
  • 数组作为函数参数后会退化为指针,函数内部不能再用 sizeof 求原数组长度;
  • 二维数组传参时必须让编译器知道列数;
  • memset(arr, 1, sizeof(arr)) 是逐字节写入,不会把每个 int 设置为1。

7. 总结

数组的核心是“同类型、连续存储、固定长度”。理解数组的内存布局,是后续学习指针、字符串、排序和动态内存管理的基础。