将8字节指针存入4字节int数组:memcpy与元素赋值的差异及失败原因
将8字节指针存入4字节整数数组时,memcpy与数组元素赋值的差异分析
问题场景
现有一个包含32个4字节整数的数组arr,需要用最后两个元素(arr[30]和arr[31])的内存空间存储一个8字节指针p。使用memcpy的写法能通过断言assert(ptr == p),但直接赋值数组元素的写法断言失败,求后者失败的原因。
两种写法代码如下:
memcpy写法
// store the pointer auto address = reinterpret_cast<int*>(p); std::memcpy(&Memory[30], &address, sizeof(address)); // retrieve the pointer int *ptr; std::memcpy(&ptr, &Memory[30], sizeof(ptr)); // test - succeed assert(ptr == p);
数组元素赋值写法
// store the pointer auto address = reinterpret_cast<int*>(p); Memory[30] = address[0]; Memory[31] = address[1]; // retrieve the pointer int *ptr; std::memcpy(&ptr, &Memory[30], sizeof(ptr)); // test - failed assert(ptr == p);
失败原因分析
核心问题出在指针内存布局的字节级完整性,以及整数赋值时的类型转换副作用:
memcpy的可靠之处:
memcpy是纯粹的字节复制操作,它会把8字节指针address的每一个字节原封不动地拷贝到Memory[30]和Memory[31]对应的连续8字节内存块里,完全保留指针的原始字节结构,所以后续读取时能精准还原出原指针值。数组赋值写法的问题:
- 跨类型访问的未定义行为:把指针
p强转成int*后,通过address[0]和address[1]拆分指针的操作属于C++标准中的未定义行为——编译器可能会进行优化,导致取出的int值并非指针的原始字节内容。 - 符号扩展破坏原始字节:
Memory是int类型数组,赋值时会执行int类型的值拷贝。如果指针的某4字节段按int补码规则是负数,赋值时会触发符号扩展,高位被填充为1,直接篡改了原始的字节序列,而我们需要的是指针的原始字节,不是经过类型转换的int值。 - 内存布局的隐性篡改:即使没有符号扩展问题,赋值操作可能会引入编译器的对齐处理、值规范化等优化,导致
Memory[30]和Memory[31]的内存内容和指针的原始字节不再一致。
- 跨类型访问的未定义行为:把指针
总结:memcpy是字节级的精准复制,完全贴合需求;而数组元素赋值是基于int类型的值传递,会引入类型转换的副作用,破坏指针的原始内存布局,最终无法还原出正确的指针。
内容的提问来源于stack exchange,提问作者heturing
相关产品推荐
相关产品推荐

