次のようにすると、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 は揮発性になります。
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 は、マルチスレッドに関してほとんどまたはまったく役に立たないことを認識していますか?
volatile int* foo;
行く方法です。volatile型修飾子は、 const型修飾子と同じように機能します。整数の定数配列へのポインターが必要な場合は、次のように記述します。
const int* foo;
一方
int* const foo;
それ自体を変更できる整数への定数ポインターです。volatileも同じように機能します。
はい、うまくいきます。である実際のメモリに違いはありませんvolatile
。これは、そのメモリと対話する方法をコンパイラに伝える方法にすぎません。
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) では。:-)
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);
コンパイルエラーになります。