现代编译器下ptr+i与arr[i]性能无差别,因-O3会将arr[i]优化为*(arr+i)并做地址折叠等;arr[i]更安全易调试,仅当裸机无优化、超大数组且全程可控时才考虑指针遍历。

在现代编译器(如 GCC/Clang 启用 -O2 或 -O3)下,ptr + i 和 arr[i] 生成的汇编代码几乎完全相同,性能无差别。 所谓“指针遍历更快”是过时认知,只在未优化或极老编译器上成立;盲目改写反而易引入越界、空指针或逻辑错误。
为什么 -O3 下两者没区别?
编译器能识别 arr[i] 的本质就是 *(arr + i),且会做公共子表达式消除、地址计算折叠、循环展开等优化。只要数组边界已知(如常量大小或传入 size),它就会把下标访问转成等效指针运算,甚至进一步向量化。
-
arr[i]更安全:编译器可结合size做更准确的边界推测(尤其配合__builtin_assume或[[likely]]) -
ptr++在循环中修改指针值,若中间有分支跳转或提前break,容易导致后续*ptr访问越界 - 调试时,
arr[i]在 GDB 中更容易 inspect;而移动后的ptr值可能已偏离原始起始位置,难以回溯
真要手动优化指针遍历,必须守住这三条线
仅当满足以下全部条件时,才考虑显式用指针遍历,并需严格防护:
- 目标平台无优化能力(如某些嵌入式裸机编译器,
-O0固定) - 数组长度极大(>10⁶ 元素)且热点函数被反复调用
- 你控制整个访问流程,不混用
ptr++和*(ptr + offset)
推荐写法(安全、清晰、不易错):
立即学习“C++免费学习笔记(深入)”;
for (int *p = arr; p != arr + size; ++p) {
*p += 1;
}
避免写法:
int *p = arr;
while (p < arr + size) {
*p++ += 1; // p 被修改两次:一次自增,一次解引用,易读错
}
比“用指针还是下标”重要十倍的优化点
真正影响数组遍历性能的,从来不是语法形式,而是内存访问模式和编译器提示:
- 确保按行优先顺序访问二维数组:
matrix[i][j](而非matrix[j][i]),否则缓存命中率暴跌 - 对齐数据:用
alignas(64)让数组起始地址对齐 cache line,减少 false sharing - 告诉编译器不会越界:
__builtin_assume(p (GCC)或 <code>[[assume(p (C++23) - 避免在循环内取地址或调用非内联函数——这会阻止编译器将循环向量化
最常被忽略的一点:sizeof(arr) 在函数参数中永远失效——传入函数的数组名会退化为指针,sizeof 返回的是指针大小,不是数组长度。这意味着任何依赖 sizeof 推导边界的“优化”在函数内部都会静默出错。


















