有一个人刚学 C写了这么一段代码int arr[5] {10, 20, 30, 40, 50}; int *p arr; printf(%p\n, p); // 0x1000 printf(%p\n, p 1); // 0x1004 ? 还是 0x1001 ?Copy他盯着输出看了半天然后问“指针不是存地址的吗地址加1不是应该加1个字节吗为什么加了 4 个字节”这个问题差不多每个学 C 的人都想过。答案是指针算术的步长由它指向的类型决定。int *p; // p 1 跳过 sizeof(int) 4 字节 char *p; // p 1 跳过 sizeof(char) 1 字节 double *p; // p 1 跳过 sizeof(double) 8 字节CopyC 语言的设计思路是指针加 1 的意思是指向下一个元素不是指向下一个字节。如果你有一个 int 数组p 指向 arr[0]那 p 1 应该指向 arr[1]而不是 arr[0] 的中间某个字节。所以编译器自动帮你跳过了 4 个字节。你可以把指针想象成一个带尺寸标尺的箭头。int* 的标尺刻度是 4 字节一格char* 的标尺刻度是 1 字节一格。同样走一格int* 跨过的距离是 char* 的四倍。但这也意味着指针算术很容易踩坑。比如把 int* 强转成 char* 再运算int arr[5] {10, 20, 30, 40, 50}; int *p arr; char *cp (char *)p; printf(%p\n, p); // 0x1000 printf(%p\n, p 1); // 0x1004 —— 跳过 4 字节 printf(%p\n, cp); // 0x1000 printf(%p\n, cp 1); // 0x1001 —— 只跳过 1 字节Copy同一个地址同一个 1 操作结果完全不一样。如果你没意识到 p 和 cp 的类型不同在 cp 1 的地方按 int 去读数据——读到的就不是 arr[1] 了而是 arr[0] 的后三个字节拼上 arr[1] 的第一个字节。更隐蔽的情况出现在 void* 上void *vp arr; vp; // 编译报错void 的大小未知编译器不知道怎么跳CopyGCC 在某些扩展模式下允许 void* 的算术当作 1 字节但标准 C 不允许——因为编译器不知道 void 有多大。还有一个经典陷阱是结构体指针struct Student { char name[20]; int age; float score; }; struct Student s[3]; struct Student *sp s;Copysp 1 跳过的是整个 struct Student 的大小——不是 20不是 24而是经过内存对齐后的实际大小。你手动算字节偏移算出 28实际可能是 32。这是因为编译器在结构体成员之间插入了填充字节来对齐内存。依赖结构体内部布局写死偏移量的代码换个编译器或者换个编译选项就直接崩了。这类 bug 在嵌入式、序列化、网络协议解析的代码里很常见。理解指针算术的步长核心就一句话指针1 的意思是指向下一个同类型的元素而不是地址值加 1。编译器根据指针类型自动计算跳过多少字节。类型不同步长不同结果不同。你觉得 C 语言这样设计是好是坏有没有试过在指针算术上翻车来评论区聊聊。