非常感谢 wallyk,我能够设计一些代码,使用他的方法生成一些程序集,向自己证明不同指针方法之间的区别。
使用代码:并使用 -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。
==
当 p 是可变指针时
int * volatile p;
==============
L3:
movq _p(%rip), %rax
movl 8(%rax), %eax
testl %eax, %eax
jne L3
xorl %eax, %eax
leave
ret
在这里,指针 p 每次循环迭代都会重新加载,因此数组项也会重新加载。但是,如果我们想要一个 volatile 整数数组,这是不正确的,因为这是可能的:
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);
会产生编译错误。