我认为以下内容会给我 10 个易失整数
volatile int foo[10];
但是,我认为以下内容不会做同样的事情。
volatile int* foo;
foo = malloc(sizeof(int)*10);
如果我对此有误以及如何使用 malloc 获得易失性的项目数组,请纠正我。
谢谢。
我认为以下内容会给我 10 个易失整数
volatile int foo[10];
但是,我认为以下内容不会做同样的事情。
volatile int* foo;
foo = malloc(sizeof(int)*10);
如果我对此有误以及如何使用 malloc 获得易失性的项目数组,请纠正我。
谢谢。
int volatile * foo;
从右到左读取“foo 是指向 volatile int 的指针”
所以无论你通过 foo 访问什么 int,这个 int 都是易变的。
附言
int * volatile foo; // "foo is a volatile pointer to an int"
!=
volatile int * foo; // foo is a pointer to an int, volatile
意思是 foo 是易变的。第二种情况实际上只是一般从右到左规则的遗留物。要吸取的教训是养成使用的习惯
char const * foo;
而不是更常见的
const char * foo;
如果您想要更复杂的事情,例如“指向函数的指针,返回指向 int 的指针”,那么就有意义了。
PS,这是一个大问题(也是我添加答案的主要原因):
我注意到您将“多线程”作为标签包含在内。您是否意识到 volatile 在多线程方面几乎/没有什么好处?
volatile int* foo;
是要走的路。volatile类型限定符的工作方式与const类型限定符一样。如果你想要一个指向整数常量数组的指针,你可以这样写:
const int* foo;
然而
int* const foo;
是一个常量指针,指向一个本身可以改变的整数。volatile的工作方式相同。
是的,这会奏效。实际内存没有什么不同volatile
。这只是告诉编译器如何与该内存交互的一种方式。
我认为第二个声明指针是可变的,而不是它指向的。要做到这一点,我认为它应该是
int * volatile foo;
这种语法是可以接受的gcc
,但我很难说服自己它有什么不同。
我发现gcc -O3
(完全优化)有所不同。对于这个(愚蠢的)测试代码:
volatile int v [10];
int * volatile p;
int main (void)
{
v [3] = p [2];
p [3] = v [2];
return 0;
}
使用volatile
, 并省略不改变的 (x86) 指令:
movl p, %eax
movl 8(%eax), %eax
movl %eax, v+12
movl p, %edx
movl v+8, %eax
movl %eax, 12(%edx)
如果没有 volatile,它会跳过重新加载p
:
movl p, %eax
movl 8(%eax), %edx ; different since p being preserved
movl %edx, v+12
; 'p' not reloaded here
movl v+8, %edx
movl %edx, 12(%eax) ; p reused
在进行了许多试图找出差异的科学实验之后,我得出的结论是没有差异。 volatile
关闭与变量相关的所有优化,这些优化将重用随后的设置值。至少使用 x86 gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33)。:-)
非常感谢 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 是 volatile 指针
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);
会产生编译错误。