C Language
型修飾子
サーチ…
備考
型修飾子は、型に関する追加のセマンティクスを記述するキーワードです。それらは型シグニチャの不可欠な部分です。宣言の最上位レベル(識別子に直接影響を及ぼす)またはサブレベル(ポインターのみに関連し、ポイント先の値に影響する)の両方に現れることがあります。
キーワード | 備考 |
---|---|
const | 宣言されたオブジェクトの突然変異(最上位に現れる)を防ぎます。またはポインター付きの値の突然変異を防止します(ポインタのサブタイプの隣に現れます)。 |
volatile | プログラム制御フローの結果だけでなく、宣言されたオブジェクト(最上位レベル)またはポインティングされた値(ポインタサブタイプ内)が外部条件の結果として値を変更する可能性があることをコンパイラに通知します。 |
restrict | ポインターのみに関連する最適化ヒント。ポインターの存続期間中に、同じポインティング先オブジェクトにアクセスするために他のポインターが使用されないという意図を宣言します。 |
記憶域クラス指定子( static
、 extern
、 auto
、 register
)、型修飾子( signed
、 unsigned
、 short
、 long
)と型指定子( int
、 char
、 double
など)に対する型修飾子の順序付けは強制されません。良い習慣は、上記の順序でそれらを置くことです:
static const volatile unsigned long int a = 5; /* good practice */
unsigned volatile long static int const b = 5; /* bad practice */
トップレベル資格
/* "a" cannot be mutated by the program but can change as a result of external conditions */
const volatile int a = 5;
/* the const applies to array elements, i.e. "a[0]" cannot be mutated */
const int arr[] = { 1, 2, 3 };
/* for the lifetime of "ptr", no other pointer could point to the same "int" object */
int *restrict ptr;
ポインタのサブタイプの資格
/* "s1" can be mutated, but "*s1" cannot */
const char *s1 = "Hello";
/* neither "s2" (because of top-level const) nor "*s2" can be mutated */
const char *const s2 = "World";
/* "*p" may change its value as a result of external conditions, "**p" and "p" cannot */
char *volatile *p;
/* "q", "*q" and "**q" may change their values as a result of external conditions */
volatile char *volatile *volatile q;
変更不可能な変数
const int a = 0; /* This variable is "unmodifiable", the compiler
should throw an error when this variable is changed */
int b = 0; /* This variable is modifiable */
b += 10; /* Changes the value of 'b' */
a += 10; /* Throws a compiler error */
const
資格は、データを変更する権利がないことを意味します。値が私たちの背後で変わることはできないということではありません。
_Bool doIt(double const* a) {
double rememberA = *a;
// do something long and complicated that calls other functions
return rememberA == *a;
}
*a
他の呼び出しの実行中に*a
が変更され*a
可能性がありますので、この関数はfalse
またはtrue
返すことがありtrue
。
警告
const
持つ変数は、ポインタを使用して変更することもできます。
const int a = 0;
int *a_ptr = (int*)&a; /* This conversion must be explicitly done with a cast */
*a_ptr += 10; /* This has undefined behavior */
printf("a = %d\n", a); /* May print: "a = 10" */
しかし、そうすることで、未定義の動作につながるエラーが発生します。ここでの難しさは、単純な例ではこれが期待通りに動作するかもしれないが、コードが大きくなると間違ってしまうことです。
揮発性変数
volatile
キーワードは、プログラム制御フローの結果だけでなく、変数の値が外部条件の結果としていつでも変更される可能性があることをコンパイラに通知します。
コンパイラは、volatile変数と関係のあるものは最適化しません。
volatile int foo; /* Different ways to declare a volatile variable */
int volatile foo;
volatile uint8_t * pReg; /* Pointers to volatile variable */
uint8_t volatile * pReg;
volatile変数を使用する主な理由は2つあります。
- メモリマップされたI / Oレジスタを持つハードウェアとのインターフェイス。
- プログラム制御フローの外で修正された変数を使用する場合(例えば、割り込みサービスルーチンで)
この例を見てみましょう:
int quit = false;
void main()
{
...
while (!quit) {
// Do something that does not modify the quit variable
}
...
}
void interrupt_handler(void)
{
quit = true;
}
コンパイラは、whileループがquit
変数を変更してループを無限while (true)
ループに変換しないことに気付くことができます。 SIGINT
とSIGTERM
ハンドラでquit
変数が設定されていても、コンパイラはそれを認識しません。
volatile
としてquit
を宣言すると、コンパイラはループを最適化しないように指示し、問題は解決されます。
この例のように、ハードウェアにアクセスするときにも同じ問題が発生します。
uint8_t * pReg = (uint8_t *) 0x1717;
// Wait for register to become non-zero
while (*pReg == 0) { } // Do something else
オプティマイザの動作は、値が常に同じであるため、変数の値を一度読み取ることです。読み直す必要はありません。だから我々は無限ループに終わる。コンパイラに必要な処理を強制するために、宣言を次のように変更します。
uint8_t volatile * pReg = (uint8_t volatile *) 0x1717;