编程C 开发者

描述 C 语言中指针算术的特点和潜在问题。这种算术基于什么,可能会出现什么意外情况,以及如何正确避免这些问题?

用 Hintsage AI 助手通过面试

答案。

问题历史:

指针算术在 C 语言中出现是为了有效地操作内存、数组和结构体。它与内存寻址紧密相关,以及 C 如何在最低级别上工作——对指针进行加减可以访问数组中的连续元素。

问题:

主要的困难在于,指针算术与数字算术并不等同:对指针增加 1 会根据它所指向的类型大小来增加其值。经典错误包括超出分配数组的边界、操作不兼容类型的指针以及尝试与 void * 进行计算。

解决方案:

在操作指针时,总是要考虑类型大小,避免与 void* 的代数计算,控制数组的边界。访问数组元素时,使用索引或计算指针,并事先检查边界。

代码示例:

#include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; int *p = arr; printf("%d\n", *(p + 2)); // 3 // 不合法:p + 10 超出数组边界 return 0; }

关键特点:

  • 对指针的增加会使地址增加 sizeof(类型),而不是增加一个字节
  • 只能比较指向同一数组元素的指针
  • 与 void* 的算术运算是不允许的(除了一些 GNU 扩展)

误导性问题。

可以将浮点类型的值或其他类型的变量加到指针上吗?

不可以,指针只能加减整数类型的值。使用浮点数将导致编译错误。

即使 i 超出数组边界,*(arr + i) 和 arr[i] 是否总是相同的?

不。它们在语义上是等价的,但如果索引超出数组边界,这两个表达式都会导致未定义行为(undefined behavior)。

从指向不同数组的指针中减去会发生什么?

结果在标准中未定义,并被视为错误。只能减去位于同一数组(或同一分配块内存)中的指针。

常见错误和反模式

  • 在操作指针时超出数组边界(buffer overrun
  • 在没有显式转换的情况下进行 void* 算术运算
  • 使用指针访问已释放的内存

生活中的示例

在代码中,开发人员使用指针算术遍历数组:

优点:

  • 在某些架构中比索引更快速。

缺点:

  • 同时忘记检查边界——导致内存损坏(segmentation fault)。

在重构版本中,每一步都使用显式的边界检查:

优点:

  • 确保不会超出数组边界

缺点:

  • 代码变得稍微长一些,并且需要开发辅助函数