const ポインターと volatile ポインター
const キーワードおよび volatile キーワードは、ポインターの処理方法を変更します。 const キーワードは、初期化後にポインターを変更できないことを指定します。それ以降、ポインターは変更から保護されます。
volatile キーワードは、後に続く名前に関連付けられた値をユーザー アプリケーション以外の操作で変更できることを指定します。 したがって、volatile キーワードは、複数のプロセッサがアクセスできる共有メモリ内のオブジェクト、または、割り込みサービス ルーチンとの通信に使用するグローバル データ領域を宣言する場合に便利です。
名前が volatile として宣言されていると、プログラムによって名前がアクセスされるたびに、コンパイラはメモリから値を再度読み込みます。 これによって、可能な最適化が大幅に減少します。 ただし、オブジェクトの状態が予期せず変わる場合、これが予測可能なプログラムの実行を保証する唯一の方法になります。
ポインターが指すオブジェクトを const または volatile として宣言するには、次の形式の宣言を使用します。
const char *cpch;
volatile char *vpch;
ポインターの値、つまりポインターに格納されている実際のアドレスを const または volatile として宣言するには、次の形式の宣言を使用します。
char * const pchc;
char * volatile pchv;
C++ 言語では、const として宣言されたオブジェクトまたはポインターを変更する代入は回避されます。 このような代入を実行すると、オブジェクトまたはポインターを宣言したときの情報が削除されるため、元の宣言の意図に反することになります。 次に宣言の例を示します。
const char cch = 'A';
char ch = 'B';
前に宣言した 2 つのオブジェクト (const char 型の cch と char 型の ch) では、次の宣言と初期化が有効です。
const char *pch1 = &cch;
const char *const pch4 = &cch;
const char *pch5 = &ch;
char *pch6 = &ch;
char *const pch7 = &ch;
const char *const pch8 = &ch;
次の宣言と初期化はエラーになります。
char *pch2 = &cch; // Error
char *const pch3 = &cch; // Error
pch2 の宣言は、定数オブジェクトを変更する可能性があるポインターを宣言するため、許可されません。 pch3 宣言は、オブジェクトではなく pointer が定数であることを指定します。この宣言は、pch2 宣言が許可されないのと同じ理由で許可されません。
次の 8 つの代入は、ポインターを介した代入と、前に宣言したポインター値の変更を示しています。ここでは、初期化が pch1 から pch8 まで正しかったと仮定します。
*pch1 = 'A'; // Error: object declared const
pch1 = &ch; // OK: pointer not declared const
*pch2 = 'A'; // OK: normal pointer
pch2 = &ch; // OK: normal pointer
*pch3 = 'A'; // OK: object not declared const
pch3 = &ch; // Error: pointer declared const
*pch4 = 'A'; // Error: object declared const
pch4 = &ch; // Error: pointer declared const
volatile として宣言されたポインター、または const と volatile の組み合わせとして宣言されたポインターは、同じ規則に従います。
const オブジェクトへのポインターは、次のような関数宣言でよく使用されます。
errno_t strcpy_s( char *strDestination, size_t numberOfElements, const char *strSource );
前のステートメントは関数 strcpy_s を宣言します。ここで、3 つの引数のうち 2 つは char へのポインター型です。 引数は値ではなく参照で渡されるため、strSource が const として宣言されていない場合、関数は strDestination と strSource の両方を自由に変更できます。 strSource を const として宣言すると、呼び出された関数によって strSource が変更されないことを呼び出し元に対して保証できます。
注意
typename* から consttypename* への標準変換があるため、strcpy_s に char * 型の引数を渡すことは可能です。ただし、この逆は真ではありません。オブジェクトまたはポインターから const 属性を削除する暗黙の変換はありません。
ある型の const ポインターは、同じ型のポインターに代入することができます。 ただし、const ではないポインターを const ポインターに代入することはできません。 次のコードは、正しい代入と正しくない代入を示します。
// const_pointer.cpp
int *const cpObject = 0;
int *pObject;
int main() {
pObject = cpObject;
cpObject = pObject; // C3892
}
次のサンプルは、オブジェクトへのポインターへのポインターがあるとき、オブジェクトを定数として宣言する方法を示します。
// const_pointer2.cpp
struct X {
X(int i) : m_i(i) { }
int m_i;
};
int main() {
// correct
const X cx(10);
const X * pcx = &cx;
const X ** ppcx = &pcx;
// also correct
X const cx2(20);
X const * pcx2 = &cx2;
X const ** ppcx2 = &pcx2;
}