Компиляция с -O2 (или -O3, если на то пошло) и запуск этой программы дает интересные результаты на моей машине.C++ pointer weird undefined behavior
#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
То, что я делаю, пишу в куче перераспределена int
указывает p
, а затем выделение долго с адресом x
. Мой компилятор последовательно помещает длинный на тот же адрес, что и p
->x == p
. Теперь, когда я разыскиваю p
и печатаю его, он сохраняет значение 123, хотя он был переписан с длинным 456. *x
затем печатается как 456. Что еще более странно, так это то, что позже, ничего не меняя, печатайте то же значение дает ожидаемые результаты. Я думал, что это метод оптимизации, который инициализирует * x, когда он используется после печати значения *p
, что объясняет это. Однако 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 <[email protected]>
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 <[email protected]>
*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 <[email protected]>
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 <[email protected]>
........
сейчас, хотя *p
была перезаписана long
инициализации (4008dc
), он по-прежнему печатается 123.
Я надеюсь, что я сделал какой-то смысл здесь, и Спасибо за любую помощь.
, чтобы сделать себя ясным: Я пытаюсь выяснить, что происходит за кулисами, что делает компилятор, и почему полученный скомпилированный код не соответствует выходу. Я ЗНАЮ, ЧТО ЭТО НЕ УКАЗАН ПОВЕДЕНИЕ И ЧТО НИЧЕГО МОЖЕТ ПРОИЗОЙТИ. Но это означает, что компилятор может создавать любой код, а не то, что CPU будет составлять инструкции. Любые идеи приветствуются.
PS: Не беспокойтесь, я не собираюсь использовать это где-нибудь;)
EDIT: На моего друга машины (OS X), что дает ожидаемые результаты даже при оптимизации.
Вы пытаетесь осмыслить неопределенное поведение. Наслаждайтесь процессом. –
Есть ли вопрос, который я пропустил? Также ... »[...] * дает ожидаемые результаты *« hehe good one =). – luk32
@ luk32 О, правильно: D Я не задал вопрос. Я просто пытаюсь понять, что происходит за кулисами. Пока это не имеет никакого смысла. : D – sammko