C 语言数组、字符串与结构体
更新时间:2026-08-25。本文是
languages/c/主题入门层第 3 篇。数组、字符串、结构体是 C 组织数据的三种复合类型——它们决定"数据在内存里怎么排",也是理解缓存、对齐、性能的关键入口。
本文要回答的问题
- 数组下标
a[i]为什么等价于*(a + i)? - C 字符串为什么必须以
'\0'结尾?忘了会怎样? - 结构体的字段在内存里是紧挨着排的吗?为什么有"对齐"?
一、数组
1.1 定义与初始化
c
int a[5]; // 未初始化,值不确定
int b[5] = {1, 2, 3, 4, 5}; // 完整初始化
int c[5] = {1, 2}; // 部分初始化,其余为 0
int d[] = {1, 2, 3}; // 编译器推断大小为 31.2 下标访问 = 指针运算
c
int a[5] = {10, 20, 30, 40, 50};
a[2] == *(a + 2); // 都是 30数组名 a 在表达式中退化为首元素地址,a[i] 就是"首地址 + 偏移 i"的解引用。
1.3 数组越界(危险!)
c
int a[5];
a[5] = 100; // 越界!写到了 a 之后 4 字节的内存C 不检查数组越界,越界写会悄悄破坏相邻变量,是最难查的 bug 之一。详见指针与内存管理。
1.4 二维数组
c
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
m[1][2] // 6,第二行第三列二维数组在内存中是按行连续存储的(行优先),m[1] 是第二行的首地址。
二、字符串
2.1 C 字符串本质:以 '\0' 结尾的字符数组
c
char s[] = "hello"; // 实际占 6 字节:h e l l o \0
char t[6] = {'h','e','l','l','o','\0'}; // 等价写法C 没有专门的字符串类型,字符串就是以空字符 '\0' 结尾的字符数组。所有字符串函数(strlen/strcpy)都依赖这个结尾符。
2.2 常见字符串操作
c
#include <string.h>
char a[20] = "hello";
size_t len = strlen(a); // 5(不含 \0)
strcpy(a, "world"); // 拷贝(注意目标要够大)
strcat(a, "!"); // 拼接
int cmp = strcmp(a, "world");// 比较,0 表示相等2.3 字符串的坑
c
char *s = "hello"; // 指向字符串字面量(只读!)
s[0] = 'H'; // 未定义行为!字面量在 .rodata,不可写
char arr[] = "hello"; // 数组在栈上,可写
arr[0] = 'H'; // 合法- 用
char *指向的字符串字面量是只读的,修改会崩溃。 strcpy不检查目标大小,目标不够大会溢出——用strncpy或snprintf更安全。
三、结构体
3.1 定义与访问
c
struct Point {
int x;
int y;
};
struct Point p;
p.x = 10;
p.y = 20;3.2 用 typedef 简化
c
typedef struct {
int x;
int y;
} Point;
Point p; // 不用再写 struct 关键字
p.x = 10;3.3 结构体指针访问(->)
c
Point p = {10, 20};
Point *pp = &p;
pp->x = 30; // 等价于 (*pp).x = 30-> 是"通过指针访问成员"的语法糖。
3.4 结构体的内存对齐(重点,衔接性能)
c
struct A {
char c; // 1 字节
int i; // 4 字节
};
printf("%zu\n", sizeof(struct A)); // 8,不是 5!为什么是 8 不是 5?因为 int 要求 4 字节对齐,char 后面要填充 3 字节,让 int 落在 4 的倍数地址上。
c
struct B {
int i; // 4 字节
char c; // 1 字节
};
printf("%zu\n", sizeof(struct B)); // 8,末尾也填充到 4 的倍数对齐的影响:
- 字段顺序不同,结构体大小不同(影响缓存命中)。
- 对齐是 L3 内存子系统 的核心概念,直接影响性能。
3.5 结构体数组与嵌套
c
typedef struct {
char name[20];
int age;
} Person;
Person people[100]; // 结构体数组
people[0].age = 30;四、三种复合类型的内存组织对比
| 类型 | 内存布局 | 关键点 |
|---|---|---|
| 数组 | 连续、同类型 | 下标 = 基址 + 偏移 |
| 字符串 | 字符数组 + '\0' | 结尾符决定长度 |
| 结构体 | 连续、可不同类型,有对齐填充 | 字段顺序影响大小 |
五、与本站主线衔接
- 对齐与缓存:结构体字段顺序决定填充字节,影响缓存行利用率,见 L3 内存子系统。
- 数组与 TLB:大数组访问的局部性决定 TLB 命中,见 L3 TLB。
- 字符串与只读段:字符串字面量在
.rodata,见 L5 ELF。
一句话总结
数组、字符串、结构体是 C 的三种复合类型——数组下标即指针偏移、字符串靠 '\0' 定长、结构体有对齐填充;它们的"内存怎么排"直接决定了后续的缓存命中与性能,是理解底层的前提。