C++通过指针访问struct成员的程序崩溃及内存操作问题咨询
问题1:std::cout崩溃而printf正常运行的原因
你修改值的代码存在内存越界写入问题,属于C++标准定义的未定义行为:
*Adress拿到的是IntPtr的值,也就是new int[2]返回的数组首地址,偏移sizeof(int)后刚好是数组第二个元素IntPtr[1]的地址- 你将该地址强制转为
long long int*后解引用写入789,而64位环境下long long占8字节,int仅占4字节,相当于你除了修改IntPtr[1]的4字节外,还把紧跟在该元素后方的4字节内存给覆盖改写了。
printf正常运行只是巧合:你覆盖的那4字节内存刚好没有被printf的执行逻辑用到,而std::cout的内部实现(比如内部缓冲区指针、类成员数据、虚函数表等)刚好存放在被你覆盖的内存区域,调用cout时访问被破坏的内存直接触发崩溃。这不是printf更安全,只是未定义行为下的不同表现而已,换编译环境、编译参数都可能出现相反的结果。
如果你把代码里的long long int*改成int*再操作,cout和printf都会正常运行。
问题2:是否可以通过该方式删除、重分配IntPtr成员
仅在你当前的实验环境(64位系统、编译器生成标准布局struct)下可以正常运行,原理如下:
- 你定义的
obj属于C++标准规定的标准布局类,类对象的地址和第一个非静态成员的地址完全相同,因此你将&MyStruct强转为long long*解引用后拿到的确实就是IntPtr成员的值 - 64位环境下指针长度和
long long长度一致,因此你可以通过*Adress直接修改IntPtr的指向,哪怕IntPtr被声明为private也能绕开编译期的访问检查。
但要特别注意:这完全是依赖平台和编译器实现的未定义行为,换32位环境、或者struct里加了虚函数、调整了成员顺序的情况下,该写法都会直接出错,绝对不能用于生产代码。另外记得每次替换IntPtr指向的内存前,必须先delete[]原来的地址,否则会出现内存泄漏。
内容的提问来源于stack exchange,提问作者zmouch
相关产品推荐
相关产品推荐

