【问题标题】:How do I declare an array created using malloc to be volatile in c++如何在 C++ 中声明使用 malloc 创建的数组是 volatile
【发布时间】:2010-02-21 04:09:49
【问题描述】:

我认为以下将给我 10 个易失性整数

volatile int foo[10];

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

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

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

谢谢。

【问题讨论】:

标签: c++ multithreading arrays malloc volatile


【解决方案1】:
int volatile * foo;

从右到左读取“foo 是一个指向 volatile int 的指针”

所以无论你通过 foo 访问什么 int,这个 int 都会是 volatile。

附言

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 的指针”,那么它就有意义了。

P.S.,这是一个大问题(也是我添加答案的主要原因):

我注意到您将“多线程”作为标签包含在内。您是否意识到 volatile 在多线程方面几乎/没有什么好处?

【讨论】:

  • 线程间共享状态变量时必须使用volatile;特别是对于使用忙等待的并发无锁算法。例如。线程 1 将在这条指令上旋转:while(barrier);直到线程 2 设置屏障 = false。如果没有使用 volatile,那么如果编译器决定从本地寄存器而不是内存中读取屏障值,则代码可能会死锁。
  • 否 - volatile 不会为所有编译器插入内存屏障。见software.intel.com/en-us/blogs/2007/11/30/…
  • 是的,在极少数情况下 volatile 确实有好处。但是自旋锁需要一个内存屏障,或者至少在“barrier == true”之后读取的数据需要一个内存屏障。在 MSVC 上,volatile 意味着障碍,但这是非标准的。
  • 我喜欢关于如何将限定符写入声明的观察。打算开始使用它。
  • 您的意思是!= 而不是==?我觉得volatile int * foo;int volatile * foo;意思一样
【解决方案2】:
volatile int* foo;

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

const int* foo;

int* const foo;

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    【解决方案4】:

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

    int * volatile foo;
    

    gcc 可以接受这种语法,但我在convincing myself 遇到了麻烦,因为它有什么不同。

    我发现与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)。 :-)

    【讨论】:

    • 不应该反过来吗?使用 volatile,它应该强制重新加载 p。
    • 这看起来像一个编译器错误,实际上。对于 volatile,它必须从 p 读取两次,因为标准说有两次读取(左值到右值的转换)。
    • 是的。我倒过来了。现在已修复。
    • +1,证明你的陈述。 :) 很高兴偶尔看到大会。
    【解决方案5】:

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

    会产生编译错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-19
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多