一维数组在内存中连续线性排列,地址按索引递增,数组名即首地址且退化为常量指针;越界访问行为未定义,传入函数后丢失长度信息,需显式传递长度或使用哨兵值。

一维数组在内存中是连续、线性排列的,每个元素紧挨着前一个,地址按索引顺序递增。这种布局不是巧合,而是语言设计和硬件访问效率共同决定的底层事实。
连续地址空间,按类型字节对齐
定义 int arr[5] 时,编译器会分配一块连续的 20 字节(假设 int 占 4 字节)内存。首元素 arr[0] 的地址记作 base,那么:
- arr[1] 地址 = base + 4
- arr[i] 地址 = base + i × sizeof(int)
- 同理,char arr[8] 占 8 字节,相邻元素地址差为 1;double arr[3] 占 24 字节,地址差为 8
数组名即首地址,天然支持指针运算
数组名本身不占额外存储,它在多数上下文中退化为指向首元素的常量指针。例如:
- int *p = arr; —— p 指向 arr[0]
- p++ 后,p 指向 arr[1],地址增加 sizeof(int)
- &arr[0]、&arr[1]、arr、(int*)arr 在数值上都可等价使用(类型语义不同,但地址值相同)
越界访问不会报错,但行为未定义
编译器不检查下标是否合法,运行时也不会自动拦截。例如:
- int arr[3] = {1,2,3}; printf("%d", arr[5]); —— 读取的是紧邻其后内存中的随机值
- 该位置可能是其他局部变量、栈帧数据,甚至触发段错误(取决于操作系统保护)
- 调试时常见输出如 -858993460(Visual Studio 调试填充值),并非“默认值”,而是内存残留
长度不可推导,需显式传递或计算
数组一旦传入函数,就退化为指针,丢失长度信息。因此:
- 在定义作用域内可用 sizeof(arr)/sizeof(arr[0]) 计算元素个数
- 但若写成 void func(int a[]),sizeof(a) 返回的是指针大小(通常 4 或 8 字节),不是原数组长度
- 安全做法是:要么传入长度参数,要么用哨兵值(如字符串末尾的 \0),或改用带长度信息的容器(如 C++ std::array)











