externvolatileポインタとは何ですか。
extern volatile uint32 *ptr;
ここで、* ptrの動作はどうなりますか?これは実際にはどういう意味ですか?
そして、それはいつ使用されるべきですか?
私はそれについてグーグルで検索しようとしましたが、満足のいく答えが得られませんでした。この組み合わせに関する情報はあまりありません。
externキーワードとvolatileキーワードはどちらも独立して考えることができます。これらの各キーワードの役割は他のキーワードと相互作用しないため、以下のように、それぞれの説明を個別に詳しく説明できます。
externは、 ptrの実際の定義が別のモジュール(another .c
)にあることをコンパイラーに通知します。基本的に、コンパイラがptrを処理する方法に大きな変更はありません-externを使用すると、別の場所で行われるように、 ptr用にメモリ内のスペースを予約する必要がないことをコンパイラに通知するだけで、実際のメモリ位置は次のようになります。後でリンカー。.c
extern uint32 *ptr;
externを省略した場合、コンパイラは文句を言いません。ただし、後で、リンカが最終的な実行可能プログラムをビルドするためにすべてのオブジェクトモジュールをリンクしようとすると、「ptrは2回定義されています」というエラーがスローされます(すでに別のモジュールで定義されているため.c
)。
uint32 *ptr;
volatileは、 ptrが存在するメモリ位置が外部イベントによって変更/変更される可能性があることをコンパイラに通知します。また、 ptrの値が少数のスコープ内で変更されないことを考慮するなど、効率の最適化に依存するべきではありません。Cのシーケンシャルライン。このようなイベントは、CPUが上記のスコープを実行し、ptr値を変更したときに発生する非同期割り込みである可能性があります。
通常(コメントに最適化されたCの仮想アセンブリコードを使用)、REGxはCPUレジスタであり、変数y ..にはあまり関心がありません。
int x = 10;
int func() {
int y; // REG4
printf("%d\n", x); // Set REG3 = memory(x) and display x
x += 2; // Add 2 to REG3
y = x * x; // REG4 = REG3 * REG3
printf("%d %d\n", x, y); // Do printf(..., REG3, REG4)
x += 5; // REG3 = REG3 + 5
// memory(x) = REG3 (save register to memory)
return y; // return REG4
}
を表示する必要があります10, 12, 144
。効率を上げるために(メモリアクセスはレジスタアクセスよりもコストがかかります)、コンパイラはxの値を内部CPUレジスタ(REG3)に格納し、 xの新しい値を格納する最後まで安全にfuncで使用するとします(グローバル変数)からxメモリ位置へ。xは最後に17です。
しかし、プログラムがそれよりも複雑で、毎分10からxを引くクロック中断があると想像してください。中断した場合はどうなりますか...
void inter_call_by_timer_every_minute() {
x -= 10;
}
...行の直後にfuncprintf("%d\n", x);
で発生しますか?funcはREG3(10)にxを持ち、2(12)を追加し、最後に5(17)を追加して、REG3の結果をxメモリ位置(17)に格納します。割り込み効果(-10)はコンパイラの最適化によって隠されているため、これは誤りです。これは、割り込みによって行われた減算を無視して、最後にREG3からmemory(x)に値を格納するためです。正しい結果は次のとおりです。xは最初は10で、割り込みはfuncの最初の後に10を減算し(0)printf
、次に2が加算され、次に5になります。結果7。
揮発性物質の追加
volatile int x = 10;
コンパイラにfuncのx最適化を回避させます
int func() {
int y; // REG4
printf("%d\n", x); // display memory(x)
x += 2; // memory(x) += 2
y = x * x; // REG4 = memory(x) * memory(x)
printf("%d %d\n", x, y); // Do printf(..., memory(x), REG4)
x += 5; // memory(x) += 5
return y; // return REG4
}
xの値を常にメモリから読み取ります。結果は、最初のprintfの後にinter_call_by_timer_every_minuteから中断されると、 x ==7になります。
キーワードを通して私が知っている方法を説明します。extern
変数が定義範囲外で使用するために定義されていることを意味します。たとえば、ヘッダーファイルで定義し、.cファイルで使用できます。
volatile
その変数の変更は外界と一貫している必要があることを意味します。これは、同じ実行スペースを共有する他のスレッドがそれを見ることができるように、すべての更新をメインメモリにコミットする必要があることを意味します。
extern
キーワードは、他の場所で定義されている(つまり、他の.c
ファイルで定義されている)グローバル変数を宣言するために使用されます。
たとえば、プロジェクトで2つの.c
ファイルa.c
を考えてみましょうb.c
。グローバル変数はで定義されてa.c
おり、その変数はそのファイルで定義されているすべての関数でアクセスできます。2番目のファイルで同じグローバル変数にアクセスする場合はb.c
、その変数を次のように宣言する必要がありますextern
。b.c
a.c
ファイルを以下に示します
int flag = 0;
int main()
{
.......
func1();
printf("\nflag value is %d\n", flag).
.......
}
b.c
ファイルを以下に示します
extern int flag;
void func1();
{
.....
flag = 10;
.....
}
volatile
キーワードは、実行可能命令の生成中にいかなる種類の最適化も行わないようにコンパイラーに通知するために使用されます。
int flag = 0;
int main()
{
while(flag == 0);
printf("\nflag value is %d\n", flag);
return 0;
}
上記のプログラムを考えてみましょう。すべてのコンパイラはwhile(flag == 0);
asを最適化しwhile(1);
ます。コードには、そのループのflag
前に値が更新される場所がないためです。while
したがって、その変数値が他のハードウェアによって更新された場合、プログラムの実行に反映されることはありません。したがって、その変数をvolatile
以下のように宣言すると、コンパイラはその変数の最適化を実行せず、そのプログラムの動作は意図したとおりになります。
volatile int flag = 0;
ただし、プログラムの変数の値が他のハードウェアによって更新される方法がない場合は、その変数をとして宣言する必要はありませんvolatile
。可変変数の場合、CPUは、その変数にアクセスしている各命令に対してI/O操作を実行する必要があるためです。このパフォーマンスへの影響は、他のハードウェアによって更新されることのない変数について考慮する必要があります。
Externは、他の場所(おそらくヘッダーファイル)で定義されていることを意味します。Volatileは、これを最適化しようとしてはならないコンパイラー向けの情報です。