Большое спасибо Уоллику, я смог разработать некоторый код, использующий его метод для генерации некоторой сборки, чтобы доказать себе разницу между различными методами указателей.
с использованием кода: и компиляция с -03
int main (void)
{
while(p[2]);
return 0;
}
когда p просто объявляется как указатель, мы зацикливаемся на цикле, из которого невозможно выйти. Обратите внимание, что если бы это была многопоточная программа, а другой поток написал бы p [2] = 0, то программа вышла бы из цикла while и завершилась бы нормально.
int * p;
============
LCFI1:
movq _p(%rip), %rax
movl 8(%rax), %eax
testl %eax, %eax
jne L6
xorl %eax, %eax
leave
ret
L6:
jmp L6
обратите внимание, что единственная инструкция для L6 - перейти на L6.
==
когда р является летучим указателем
int * volatile p;
==============
L3:
movq _p(%rip), %rax
movl 8(%rax), %eax
testl %eax, %eax
jne L3
xorl %eax, %eax
leave
ret
здесь указатель p перезагружается при каждой итерации цикла, и, как следствие, элемент массива также перезагружается. Однако это было бы неправильно, если бы мы хотели получить массив летучих целых чисел, поскольку это было бы возможно:
int* volatile p;
..
..
int* j;
j = &p[2];
while(j);
и приведет к циклу, который невозможно завершить в многопоточной программе.
==
наконец, это правильное решение, как хорошо объяснил Тони.
int volatile * p;
LCFI1:
movq _p(%rip), %rdx
addq $8, %rdx
.align 4,0x90
L3:
movl (%rdx), %eax
testl %eax, %eax
jne L3
leave
ret
В этом случае адрес p [2] сохраняется в значении регистра и не загружается из памяти, но значение p [2] перезагружается из памяти при каждом цикле цикла.
также обратите внимание, что
int volatile * p;
..
..
int* j;
j = &p[2];
while(j);
приведет к ошибке компиляции.