标签:c undefined-behavior pointers
使用-O2(或者-O3)进行编译并运行该程序会在我的机器上产生有趣的结果.
#include <iostream>
using namespace std;
int main()
{
// Pointer to an int in the heap with a value of 5
int *p = new int(5);
// Deallocate the memory, but keep a dangling pointer
delete p;
// Write 123 to deallocated space
*p = 123;
// Allocate a long int in the heap
long *x = new long(456);
// Print values and pointers
cout << "*p: " << *p << endl;
cout << "*x: " << *x << endl;
cout << "p: " << p << endl;
cout << "x: " << x << endl;
cout << endl << "Changing nothing" << endl << endl;
// Print again without changing anything
cout << "*p: " << *p << endl;
cout << "*x: " << *x << endl;
cout << "p: " << p << endl;
cout << "x: " << x << endl;
return 0;
}
g -O2 code.cc; ./a.out
*p: 123
*x: 456
p: 0x112f010
x: 0x112f010
Changing nothing
*p: 456
*x: 456
p: 0x112f010
x: 0x112f010
我正在做的是写入p指向的堆中的deallocated int,然后分配一个带地址x的long.我的编译器始终将长整数放在与p – >相同的地址上. x == p.
现在,当我取消引用p并打印它时,它保留了123的值,即使它已被长456重写.* x然后打印为456.甚至更奇怪的是,稍后,没有改变任何东西,打印相同的值产生预期的结果.我认为这是一种优化技术,它只在打印值* p后使用时初始化* x,这可以解释它.然而,一个objdump说了别的.这是一个截断并注释的objdump -d a.out:
00000000004008a0 <main>:
4008a0: 41 54 push %r12
4008a2: 55 push %rbp
Most likely the int allocation, where 0x4 is the size (4 bytes)
4008a3: bf 04 00 00 00 mov $0x4,%edi
4008a8: 53 push %rbx
4008a9: e8 e2 ff ff ff callq 400890 <_Znwm@plt>
I have no idea what is going on here, but the pointer p is in 2 registers. Let's call the other one q.
q = p;
4008ae: 48 89 c3 mov %rax,%rbx
4008b1: 48 89 c7 mov %rax,%rdi
*p = 5;
4008b4: c7 00 05 00 00 00 movl $0x5,(%rax)
delete p;
4008ba: e8 51 ff ff ff callq 400810 <_ZdlPv@plt>
*q = 123;
4008bf: c7 03 7b 00 00 00 movl $0x7b,(%rbx)
The long allocation and some other stuff (?). (8 bytes)
4008c5: bf 08 00 00 00 mov $0x8,%edi
4008ca: e8 c1 ff ff ff callq 400890 <_Znwm@plt>
4008cf: 44 8b 23 mov (%rbx),%r12d
4008d2: be e4 0b 40 00 mov $0x400be4,%esi
4008d7: bf c0 12 60 00 mov $0x6012c0,%edi
Initialization of the long before the printing
*p = 456;
4008dc: 48 c7 00 c8 01 00 00 movq $0x1c8,(%rax)
4008e3: 48 89 c5 mov %rax,%rbp
The printing
4008e6: e8 85 ff ff ff callq 400870 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
........
现在,尽管* p已被长初始化(4008dc)覆盖,但它仍然打印为123.
我希望我在这里有任何意义,谢谢你的帮助.
让自己清楚:
我试图弄清楚幕后发生了什么,编译器做了什么,以及为什么生成的编译代码与输出不对应.我知道这是不明确的行为,而且任何事情都可能发生.但这意味着编译器可以生成任何代码而不是CPU将编写指令.欢迎任何想法.
PS:别担心,我不打算在任何地方使用它;)
编辑:在我朋友的机器(OS X)上,即使进行优化也能产生预期的结果.
解决方法:
您过早地停止查看反汇编输出(或者至少您没有发布与您的问题相关的下几行).他们可能看起来像:
movl %r12d, %esi
movq %rax, %rdi
call _ZNSolsEi
movq %rax, %rdi
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
rbx和r12是必须在Linux上GCC使用的x64 ABI中的函数调用之间保留的寄存器.在分配了long之后,你会看到这条指令:
mov (%rbx),%r12d
之前在指令流中使用rbx包括:
mov %rax,%rbx ; store the `p` pointer in `rbx`
...
movl $0x7b,(%rbx) ; store 123 where `p` pointed (even though it has been freed before)
...
mov (%rbx),%r12d ; read that value - 123 - back and into `r12`
然后你看到我在上面发布的片段,这是没有进入你的问题的反汇编,并且对应于cout的一部分<< “* p:”<< * p<< endl声明:
movl %r12d, %esi ; put 123 into `esi`, which is used to pass an argument to a function call
并打印123.
标签:c,undefined-behavior,pointers 来源: https://codeday.me/bug/20190824/1710227.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。