16

我认为以下内容会给我 10 个易失整数

volatile int foo[10];

但是,我认为以下内容不会做同样的事情。

volatile int* foo;
foo = malloc(sizeof(int)*10);

如果我对此有误以及如何使用 malloc 获得易失性的项目数组,请纠正我。

谢谢。

4

5 回答 5

12
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 在多线程方面几乎/没有什么好处?

于 2010-02-23T04:08:53.310 回答
6
volatile int* foo;

是要走的路。volatile类型限定符的工作方式与const类型限定符一样。如果你想要一个指向整数常量数组的指针,你可以这样写:

const int* foo;

然而

int* const foo;

是一个常量指针,指向一个本身可以改变的整数。volatile的工作方式相同。

于 2010-02-21T04:44:28.230 回答
3

是的,这会奏效。实际内存没有什么不同volatile。这只是告诉编译器如何与该内存交互的一种方式。

于 2010-02-21T04:11:30.947 回答
2

我认为第二个声明指针是可变的,而不是它指向的。要做到这一点,我认为它应该是

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)。:-)

于 2010-02-21T04:30:47.263 回答
1

非常感谢 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);

会产生编译错误。

于 2010-02-23T09:54:28.450 回答