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 は揮発性になります。

PS

int * volatile foo; // "foo is a volatile pointer to an int"

!=

volatile int * foo; // foo is a pointer to an int, volatile

つまり、 foo は揮発性です。2 番目のケースは、一般的な右から左へのルールの単なる残り物です。学ぶべき教訓は、使用する習慣を身につけることです

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

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 が揮発性ポインタの場合

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);

コンパイルエラーになります。

于 2010-02-23T09:54:28.450 に答える