执行MemCopy2函数后指针地址为何变化?添加局部变量后地址不变的原因
内存拷贝后指针地址异常的原因解析
问题背景
我编写了MemCopy2函数,用于从内存区域拷贝数据到另一区域,为避免内存重叠,采用从数组高位到低位的顺序拷贝。代码如下:
#include<stdio.h> #include<stdint.h> void MemCopy2(uint8_t * Src, uint8_t * Dest, uint32_t Len) { uint32_t count; for (count = Len; count > 0; count--) { Dest[count-1] = Src[count-1]; } } int main() { uint32_t A[2] = { 0xABCD1234, 0x567890AB }; uint32_t * Ptr = (uint16_t*)&A + 1; // CD printf("%d \n", Ptr); MemCopy2((uint8_t*)&A, (uint8_t*)Ptr, sizeof(A)); printf("%d \n", Ptr); return 0; }
现象描述
执行MemCopy2函数前后打印Ptr的地址,结果为:
6487570 6444664
可见Ptr的地址发生了变化。但如果在main函数中数组A初始化前添加局部变量int i=6;,再次执行后打印结果为:
6487554 6487554
此时Ptr的地址保持不变。我猜测可能与栈的入栈出栈、局部变量的存储有关,想知道具体原因。
原因分析
核心问题是代码存在内存越界的未定义行为,直接修改了Ptr变量本身,具体细节如下:
指针计算的偏移逻辑
代码里uint32_t * Ptr = (uint16_t*)&A + 1;的逻辑是:把数组A的地址强制转为uint16_t*类型后,偏移1个uint16_t的长度(即2字节)。最终Ptr指向的是数组A起始地址+2字节的位置,也就是A[0]的第3、4字节(对应数值0xCD的存储位置)。栈布局的差异导致不同结果
C语言中局部变量存在栈上,栈的增长方向通常是从高地址向低地址:- 无
int i=6;时,栈的布局大概是[函数返回地址][Ptr变量][数组A](地址从高到低排列)。sizeof(A)是8字节,当执行MemCopy2从A的起始地址拷贝8字节到Ptr指向的位置时,拷贝的范围是A+2到A+10字节——这个范围刚好覆盖了Ptr变量的存储位置,所以拷贝过程中Ptr的值被修改,打印出的地址自然变化。 - 添加
int i=6;后,栈布局变为[函数返回地址][Ptr变量][int i][数组A]。此时拷贝的8字节范围A+2到A+10只会覆盖数组A和变量i的区域,碰不到Ptr的存储位置,所以Ptr的值不会被修改,打印地址保持不变。
- 无
未定义行为的本质
你的代码中,目标地址Dest和源地址Src存在重叠,且拷贝长度超出了数组A的边界,属于非法的内存越界访问。这种行为在C语言中是未定义的——不同的栈布局、编译器优化都会导致不同的运行结果,这就是为什么添加一个局部变量就会出现完全不同的表现。
内容的提问来源于stack exchange,提问作者Nguyễn Minh Tiến
相关产品推荐
相关产品推荐

