#チュートリアルの推奨事項: 「C 言語における volatile キーワードの役割: 後で定義された変数がいつでも変更される可能性があることをコンパイラに思い出させるため、コンパイルされたプログラムでこの変数を保存または読み取る必要があるたびに、そのことをコンパイラに伝えます。この変数が最適化されていない場合、データは変数メモリ アドレスから直接読み取られるため、特別なアドレスへの安定したアクセスが提供され、エラーが回避されます。
c 言語チュートリアル ビデオ」
1. はじめに
1. コンパイラの最適化の概要:
メモリ アクセス速度は CPU の処理速度に比べてはるかに遅いため、コンパイラの最適化を改善するにはハードウェアにおけるマシンの全体的なパフォーマンス ハードウェア キャッシュ キャッシュを導入して、メモリへのアクセスを高速化します。また、現代の CPU では命令の実行が必ずしも厳密な順序に従っているわけではなく、CPU の命令パイプラインを最大限に活用して実行速度を向上させるために、相関関係のない命令を順不同で実行することもできます。上記はハードウェアレベルの最適化です。ソフトウェア レベルの最適化を見てみましょう。1 つはコードを記述するときにプログラマによって最適化され、もう 1 つはコンパイラによって最適化されます。 コンパイラの最適化に一般的に使用される方法には、メモリ変数をレジスタにキャッシュする、CPU 命令パイプラインを最大限に活用するために命令の順序を調整するなどがあります。一般的な方法は、読み取り命令と書き込み命令の順序を変更することです。従来型メモリを最適化する場合、これらの最適化は透過的で非常に効率的です。コンパイラの最適化またはハードウェアの並べ替えによって引き起こされる問題の解決策は、ハードウェア (または他のプロセッサ) の観点から特定の順序で実行する必要がある操作の間にメモリ バリアを配置することです。Linux はマクロ ソリューションを提供します。コンパイラの実行順序の問題。
void Barrier(void)
この関数は、メモリ バリアを挿入するようにコンパイラに通知しますが、ハードウェアでは無効です。コンパイルされたコードは、現在の CPU レジスタ内のすべての変更された値をメモリに保存しますこれらが必要です データが必要な場合は、再度メモリから読み出します。
2. Volatile は常に最適化に関係しており、コンパイラにはデータ フロー分析と呼ばれる技術があり、プログラム内の変数がどこに割り当てられ、どこで使用され、どこで変数が使用されているかを分析します。失敗と分析結果 定数のマージ、定数の伝播、その他の最適化に使用でき、さらに一部のコードを削除できます。ただし、プログラムによってはこれらの最適化が必要ない場合もあります。この場合、 volatile キーワードを使用してこれらの最適化を禁止できます。
##2. volatile の詳細説明: ##1. 原理と機能:
#Volatile の本来の意味は「揮発性」です。レジスタへのアクセスはメモリ ユニットへのアクセスよりもはるかに高速であるため、一般にコンパイラはメモリへのアクセスを減らすために最適化を実行しますが、ダーティ データが読み取られる可能性があります。
volatile を使用して変数値を宣言する必要がある場合 、システムは常にデータが配置されているメモリからデータを再読み取りします
。前の命令はそこからそれを読み取るだけです データは読み取られました。正確には、このキーワードで宣言された変数が見つかった場合、コンパイラはその変数にアクセスするコードを最適化しなくなります (データは変数のメモリ アドレスから直接読み取られます)。これにより、特別なアドレスへの安定したアクセスが提供され、volatile が使用されない場合、コンパイラは宣言されたステートメントを最適化します。 (簡単に言えば、volatile キーワードはコンパイラの結果に影響します。volatile で宣言された変数は、変数がいつでも変更される可能性があることを意味します。エラーを避けるために、変数に関連する操作に対してコンパイルの最適化を実行しないでください)
2. 2 つの例を見てください:
1>
コンパイラ に最適化を行わないよう指示しますたとえば、2 つの命令を特定のアドレスに送信する場合:
int *ip =...; //设备地址 *ip = 1; //第一个指令 *ip = 2; //第二个指令
int *ip = ...; *ip = 2;#結果として、最初の命令が失われます。 volatile を使用すると、コンパイラーは最適化を実行できなくなり、プログラムの本来の意図が保証されます。
volatile int *ip = ...; *ip = 1; *ip = 2;
コンパイラーに最適化を実行させたい場合でも、価値明細書は 1 回に減ります。他の最適化のみが可能です。
2>
で定義された変数はプログラムの外部で変更されるため、毎回メモリから読み取る必要があり、読み込むことはできません。キャッシュまたはレジスタに配置されたバックアップを再利用します。 ###############例えば:#########
volatile char a; a=0; while(!a){ //do some things; } doother();
如果没有 volatiledoother()不会被执行
3.下面是使用volatile变量的几个场景:
1>中断服务程序中修改的供其它程序检测的变量需要加volatile;
例如:
static int i=0; int main(void) { ... while (1){ if (i) dosomething(); } } /* Interrupt service routine. */ void ISR_2(void) { i=1; }
程序的本意是希望ISR_2中断产生时,在main函数中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
2>多任务环境下各任务间共享的标志应该加volatile
3>存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。
例如:
假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。
int *output = (unsigned int *)0xff800000;//定义一个IO端口; int init(void) { int i; for(i=0;i< 10;i++){ *output = i; } }
经过编译器优化后,编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output这个指针赋值为9,所以编译器最后给你编译编译的代码结果相当于:
int init(void) { *output = 9; }
如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值,显然优化过程并不能达到目的。反之如果你不是对此端口反复写操作,而是反复读操作,其结果是一样的,编译器在优化后,也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。这时候就该使用volatile通知编译器这个变量是一个不稳定的,在遇到此变量时候不要优化。
例如:
volatile int *output=(volatile unsigned int *)0xff800000;//定义一个I/O端口
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中禁止任务调度,3中则只能依靠硬件的良好设计。
4.几个问题
1)一个参数既可以是const还可以是volatile吗?
可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2) 一个指针可以是volatile 吗?
可以,当一个中服务子程序修该一个指向一个buffer的指针时。
5.volatile的本质:
1> 编译器的优化
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。
当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致。
当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致。
2>volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人。
6.下面的函数有什么错误:
int square(volatile int *ptr) { return *ptr * *ptr; }
该程序的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
注意:频繁地使用volatile很可能会增加代码尺寸和降低性能,因此要合理的使用volatile。
更多编程相关知识,请访问:编程教学!!
以上がC言語のvolatileキーワードの機能は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。