定数って使ってますか?
C言語で定数というと「#define」を使って定義するイメージが強いですよね。でも、const修飾子を使うとひとつの関数内のみで定数を扱う場合などに便利です。
この記事では、constについて
- constとは
- constの使い方
- constを使う上での注意点について
など応用的な内容についても解説していきます。今回はconstについて、使い方をわかりやすく解説します!
constとは
constとは、変数の値を変更せず定数として宣言する際に使う修飾子です。constが付くと変数は書き換えができなくなり、読み取り専用となります。値を変更しようとするとコンパイルエラーが発生します。
const修飾子は変数の型の前に記述します。
また、const修飾子はポインタ変数に対しても使用します。ポインタ変数とは変数のアドレスを記憶する変数のことです。
ポインタ変数のconst修飾子を記述する位置には以下の3種類があります。
- 変数の型の前
- アスタリスクと変数名の間
- 上記2つの併用
それぞれで挙動が異なりますので、後でくわしく解説します。
constの使い方
それではconstの使い方についてサンプルコードを確認していきましょう。
constは変数を修飾する場合とポインタ変数を修飾する場合と2種類ありますので、分けて確認していきます。
変数を修飾する場合
変数を修飾する場合は、変数の型の前にconstを記述します。constを記述し初期化した変数の値を変更しようとするとコンパイルエラーが発生します。
それではサンプルコードで確認していきましょう。
#include <stdio.h> float area(float rad, const float pi) { /* error: assignment of read-only parameter 'pi' pi = 3.0f; */ rad = 3.0f; return rad * rad * pi; } int main(void) { const float pi = 3.14f; const int array[3] = {0, 1, 2}; const char greeting[] = "Hello"; /* error: assignment of read-only parameter 'pi' pi = 3.0f; */ /* error: assignment of read-only location 'array[0]' array[0] = 1; */ /* error: assignment to expression with array type greeting = "Bye"; */ /* error: assignment of read-only location 'greeting[0]' greeting[0] = 'h'; */ const char *pref = "Tokyo"; pref = "Osaka"; printf("%s", pref); return 0; }
実行結果:
Osaka
このサンプルコードではfloat型、int型配列、char型配列(文字列)についてconst修飾子を付けています。それぞれの変数について変更しようとするとコンパイルエラーが発生するので、コメントアウトしています。
また、area関数内ではfloat型の第2引数にconst修飾子を付けています。関数内で第2引数の値を変更しようとするとコンパイルエラーが発生するので、コメントアウトしています。
このように関数の引数にconst修飾子を付けると、関数内で値が変更されるのを防ぐことができます。main関数内の後半ではポインタ変数で文字列「Tokyo」を宣言していますが、こちらは書き換えができています。
ポインタ変数を修飾する場合はconstを記述する位置により、コンパイルの可否や実行結果が変わってくるので、詳細は次節で解説します。
ポインタ変数を修飾する場合
ポインタ変数を修飾する場合はconst修飾子の位置により挙動が変わってきます。
変数の型の前に記述する場合は、ポインタ変数自体の書き換えはできますが、ポインタ変数の要素値の書き換えができません。ポインタ変数のアスタリスクと変数名の間に記述する場合は、ポインタ変数自体の書き換えができません。
ポインタ変数の要素値にアクセスしてもコンパイルエラーにはなりませんが、値の変更はできません。変数の型の前アスタリスクと変数名の間の両方に記述する場合は、ポインタ変数自体の書き換えも要素値の書き換えもできません。
それではサンプルコードで確認していきましょう。
#include <stdio.h> int main(void) { const char *pref1 = "Tokyo"; pref1 = "Osaka"; printf("%s\n", pref1); /* error: assignment of read-only location '*pref1' pref1[0] = 't'; printf("%s\n", pref1); */ char* const pref2 = "Tokyo"; /* error: assignment of read-only variable 'pref2' pref2 = "Osaka"; printf("%s\n", pref2); */ pref2[0] = 't'; printf("%s\n", pref2); const char* const pref3 = "Tokyo"; /* error: assignment of read-only variable 'pref3' pref3 = "Osaka"; printf("%s\n", pref3); */ /* error: assignment of read-only location '*pref3' pref3[0] = 't'; printf("%s\n", pref3); */ return 0; }
実行結果:
Osaka Tokyo
このサンプルコードのポインタ変数「pref1」では変数の型の前にconstを記述しています。
ポインタ変数自体は「Osaka」に書き換えられています。
ポインタ変数の0番目の要素値を書き換えようとするとコンパイルエラーが発生するので、コメントアウトしています。ポインタ変数「pref2」ではアスタリスクと変数名の間にconstを記述しています。
ポインタ変数自体を書き換えようとするとコンパイルエラーが発生するので、コメントアウトしています。ポインタ変数の0番目の要素値を小文字の「t」に書き換えようとアクセスしてもコンパイルエラーにはなりませんが、値の変更はできず大文字の「T」のままです。
また環境によっては実行時エラーが発生することもありますので注意しましょう!
ポインタ変数「pref3」では変数の型の前とアスタリスクと変数名の間の両方にconstを記述しています。ポインタ変数自体の書き換えも要素値の書き換えもできずコンパイルエラーになりますので、コメントアウトしています。
constを使う上での注意点について
constを実際に使う上で注意しなければならないことが、2つほどあります。
それぞれ説明していきます。
キャストによる変更
const修飾はキャストにより外すこともできます。外す例をサンプルコードを使って解説しますが、外すべきかそうでないかは注意して使うようにしましょう!
それではサンプルコードを確認していきましょう。
#include <stdio.h> int main(void) { const char greeting[] = "Hello"; const char* greeting2 = (const char*)greeting; // ポインタへのキャスト char* greeting3 = (char*)greeting2; // constを外す greeting3[0] = 'h'; printf("%s\n", greeting3); const char *pref = "Tokyo"; char* pref2 = (char*)pref; // constを外す //pref2[0] = 't'; // 実行時エラー printf("%s\n", pref2); return 0; }
実行結果:
hello Tokyo
このサンプルコードではchar型配列(文字列)のconstを外す方法を説明します。
まずchar型配列(文字列)を定数ポインタへキャストしています。さらに定数ポインタをキャストしてconstを外しています。通常のchar型のポインタの0番目の要素値を小文字の「h」に書き換えています。文字列を表示出力すると頭文字が小文字に書き換えられ出力表示されています。
なお、ポインタ型文字列のconstを外そうともしていますが、コンパイルは出来ますが同様に要素値を書き換える際に実行時エラーが発生します。ご注意ください!
名前の大文字定義による変更
const修飾子を付けると読み込み専用で定数扱いとなります。
定数扱いの場合は宣言する際に大文字で記述するのが慣例となっていますが、C言語の場合はdefineマクロで定数を宣言する場合に大文字表記で行います。
例) #define SIZE 100
const修飾子を付けて仮に大文字で宣言すると、defineマクロでの宣言と名前の衝突が発生し値が書き換えられる危険性があるので、const修飾子を付けて宣言する場合は小文字で命名します。
例) const int size = 100;
注意しましょう!
まとめ
ここでは、constの使い方について説明しました。
ひとつの関数内のみで定数を扱う場合に使用すると便利です。
ポインタ変数に使う場合の記述する位置やキャストによる外れ、define定義との名前の衝突など注意すべき点もありますので使いこなすことができるように、この記事を何度も参考にして下さいね!