为何指针算术运算结果错误?4096字节偏移计算异常
指针偏移计算错误的原因分析
我尝试每次将指针向前移动4096字节,编写的代码如下:
#include <stdint.h> #include <stdio.h> void calculate(void* a) { uintptr_t stride = 4096; uint64_t *buff = (uint64_t*)a; for (uintptr_t offset = 0; offset < 20000; offset += stride) { fprintf(stdout, "%s: stride = 0x%x offset = 0x%x buff = %p offset+buff = %p\n", __func__, stride, offset, buff, offset+buff); } } int main(void) { void* address = 0x7f5b588d1000; calculate(address); return 0; }
但运行后输出结果为:
calculate: stride = 0x1000 offset = 0x0 buff = 0x7f5b588d1000 offset+buff = 0x7f5b588d1000 calculate: stride = 0x1000 offset = 0x1000 buff = 0x7f5b588d1000 offset+buff = 0x7f5b588d9000
可以看到计算结果显示:
0x7f5b588d1000 + 0x1000 = 0x7f5b588d9000
而预期结果应为0x7f5b588d2000,请问该计算错误的原因是什么?
错误原因
问题核心是C语言指针算术的自动缩放特性:
- 你将
void*类型的输入指针强制转换为uint64_t*类型,而指针的偏移运算会根据其指向的类型大小自动调整偏移量。 uint64_t占8字节,所以当你执行offset + buff时,编译器会把offset的值当成「uint64_t元素的个数」,而非字节数。- 你的
stride是4096(即0x1000),实际计算为:0x7f5b588d1000 + 0x1000 * 8 = 0x7f5b588d1000 + 0x8000 = 0x7f5b588d9000,这就是输出结果的由来。
修复方案
有两种直接的修复方式:
- 使用
char*类型进行字节级偏移(char的大小是1字节,偏移不会被缩放):
#include <stdint.h> #include <stdio.h> void calculate(void* a) { uintptr_t stride = 4096; char* buff = (char*)a; // 转为char*,确保按字节偏移 for (uintptr_t offset = 0; offset < 20000; offset += stride) { fprintf(stdout, "%s: stride = 0x%x offset = 0x%x buff = %p offset+buff = %p\n", __func__, stride, offset, buff, buff + offset); } } int main(void) { void* address = 0x7f5b588d1000; calculate(address); return 0; }
- 若必须使用
uint64_t*,则将字节步长转换为元素个数:
#include <stdint.h> #include <stdio.h> void calculate(void* a) { uintptr_t byte_stride = 4096; uint64_t *buff = (uint64_t*)a; uintptr_t elem_stride = byte_stride / sizeof(uint64_t); // 计算每个步长对应的元素个数 for (uintptr_t offset = 0; offset < 20000 / sizeof(uint64_t); offset += elem_stride) { // 输出时转换回字节数显示 fprintf(stdout, "%s: stride = 0x%x offset = 0x%x buff = %p offset+buff = %p\n", __func__, (unsigned int)byte_stride, (unsigned int)(offset * sizeof(uint64_t)), buff, buff + offset); } } int main(void) { void* address = 0x7f5b588d1000; calculate(address); return 0; }
内容的提问来源于stack exchange,提问作者ohiliouh
相关产品推荐
相关产品推荐

