C言語の基本|constの使い方

変えない値にはconstという鍵をかけて、思わぬ書き換えによるバグを防ごう。

C言語のプログラムでは、処理の途中で値が変化する変数だけでなく、最初に設定した値を最後まで変更したくない場合もあります。

たとえば、次のような値です。

  • 消費税率
  • 配列に格納できる最大要素数
  • システムが処理できる最大接続数
  • 1週間の日数
  • 計算で使用する基準値や係数

このような固定値を通常の変数として宣言すると、プログラムの途中で誤って別の値を代入しても、そのまま処理できてしまいます。

書き換えに気づかずプログラムを実行すると、計算結果や判定結果がずれてしまうかもしれません。しかも、値が変更された場所と問題が発生した場所が離れていると、原因を見つけるのが難しくなります。

そこで使用するのがconstです。

変数の宣言にconstを付けると、その変数は初期化後に書き換えられなくなります。誤って別の値を代入しようとした場合は、コンパイラが問題を検出してくれます。

constは、変更してはいけない値を守るための安全装置として利用できます。

constとは

constは、変数の性質を指定する型修飾子です。

通常の変数は、宣言した後に何度でも値を変更できます。一方、constを付けた変数は、最初に設定した値を読み取ることはできますが、後から別の値を代入することはできません。

通常の変数を自由に中身を入れ替えられる箱とするなら、constを付けた変数は、初期値を入れた後に鍵をかけた箱と考えられます。

  • 保存されている値を読み取れる
  • 計算式や条件式で利用できる
  • printfで値を表示できる
  • 初期化後に別の値を代入できない
  • 誤って書き換えようとするとコンパイラが問題を検出する

図1:constは値を守る鍵

この図から分かること

通常の変数は、値の読み取りと書き換えの両方ができます。一方、constを付けた変数は、値を読み取ることはできますが、初期化後に書き換えることはできません。

constを付けても、値が使えなくなるわけではありません。計算、比較、表示などでは、通常の変数と同じように利用できます。

禁止されるのは、初期化後に別の値を代入する操作です。

const型修飾子の基本的な書き方

constを付けた変数は、次の形式で宣言します。

const 型名 変数名 = 初期値;

整数型の値を固定する場合は、次のように記述できます。

const int MAX_CONNECTIONS = 128;

それぞれの記述には、次の役割があります。

記述役割
const初期化後の書き換えを禁止する
int保存する値を整数型にする
MAX_CONNECTIONS変数の名前を指定する
128宣言時に設定する初期値

この宣言では、整数型の変数MAX_CONNECTIONSを128で初期化しています。

MAX_CONNECTIONSの値は、計算や表示で何度でも読み取れます。ただし、宣言後に160などの別の値を代入することはできません。

constを付けた変数は宣言時に初期化する

constを付けた変数は、宣言と同時に初期値を設定するのが基本です。

const int MAX_CONNECTIONS = 128;

C言語の文法では、すべてのconst変数に初期値が必須というわけではありません。しかし、constを付けた変数には後から値を代入できないため、初期化せずに宣言すると扱いに困ります。

初心者のうちは、次の流れで覚えておくと安全です。

  • constを付けて変数を宣言する
  • 宣言と同時に初期値を設定する
  • 初期化した後は値を変更しない

最初から使用する値が決まっていない場合や、処理の途中で値を変更する必要がある場合は、constを付けずに通常の変数として宣言します。

constを付けた変数名を大文字にする理由

constを付けた変数には、次のような大文字の名前がよく使われます。

  • TAX_RATE
  • MAX_CONNECTIONS
  • MAX_USERS
  • DAYS_IN_WEEK
  • BUFFER_SIZE

複数の単語を組み合わせる場合は、単語の間をアンダースコアで区切ると読みやすくなります。

大文字で書くことは、C言語の文法で決められた必須ルールではありません。小文字の名前を使用しても、文法上は問題ありません。

大文字にするのは、コードを読んだ人が固定値として扱われていることを見分けやすくするためです。

変数名名前から読み取れる意味
TAX_RATE税率を表す値
MAX_CONNECTIONS最大接続数を表す値
DAYS_IN_WEEK1週間の日数を表す値
BUFFER_SIZEバッファーの大きさを表す値

意味の分かる名前を付けると、数値だけを直接書くよりも、コードの目的が伝わりやすくなります。

ただし、変数名を大文字にしただけでは、値の変更は禁止されません。実際に書き換えを禁止するのはconstの役割です。

constを使う利点

誤った書き換えを実行前に発見できる

通常の変数には、何度でも別の値を代入できます。

そのため、固定しておきたい値を通常の変数として宣言すると、誤った代入も正しい処理として受け付けられてしまいます。

constを付けておけば、値を書き換えようとした段階でコンパイラが問題を検出します。プログラムを実行する前にミスを発見できるため、不正な値を使った処理が実行されるのを防ぎやすくなります。

値を変更しない意図が伝わる

変数の宣言にconstが付いていれば、その値が処理の途中で変化しないことをコードから判断できます。

これは複数人でプログラムを開発するときに役立ちます。作成者以外の人が読んだ場合でも、変更してよい値と変更してはいけない値を区別しやすくなるからです。

時間がたってから自分のコードを読み直す場合にも、constは設計時の意図を伝える目印になります。

マジックナンバーを減らせる

計算式や条件式の中に数値を直接書くと、その数値が何を表しているのか分かりにくくなることがあります。このような、意味を読み取りにくい数値はマジックナンバーと呼ばれます。

たとえば、接続数を判定する複数の場所に128を直接記述すると、128が何の上限なのかをコードだけで判断しにくくなります。

128にMAX_CONNECTIONSという名前を付ければ、システムが処理できる最大接続数であることが伝わります。

意味のある名前を付けることで、計算式や条件式も読みやすくなります。

図2:通常の変数とconstの書き換え結果

この図から分かること

active_connectionsは通常の変数なので、現在の接続状況に合わせて96から100へ変更できます。

MAX_CONNECTIONSは、システムの仕様として固定する最大接続数です。constが付いているため、128から160へ変更しようとすると、コンパイラが問題を検出します。

値を変更できるかどうかは、変数名が大文字か小文字かではなく、宣言にconstが付いているかどうかで決まります。

constを使ったプログラム

ここでは、通信システムの最大接続数をconstで管理します。

最大接続数は固定したまま、現在の接続数だけを更新します。固定値と変化する値を分けて扱うことで、constの役割を確認できます。

最大接続数と現在の接続数を表示するプログラム

ファイル名:6_12_1.c

#include <stdio.h>

int main(void)
{
    const int MAX_CONNECTIONS = 128;  /* システムの最大接続数 */
    int active_connections = 96;      /* 現在の接続数 */

    printf("最大接続数: %d件\n", MAX_CONNECTIONS);
    printf("現在の接続数: %d件\n", active_connections);
    printf("接続可能な残り件数: %d件\n",
           MAX_CONNECTIONS - active_connections);

    active_connections = 100;
    printf("更新後の接続数: %d件\n", active_connections);

    /* MAX_CONNECTIONS = 160; */  /* constのため変更できない */

    return 0;
}
実行結果の例
最大接続数: 128件
現在の接続数: 96件
接続可能な残り件数: 32件
更新後の接続数: 100件
プログラムの詳しい解説

#include <stdio.h>では、printfを使用するために標準入出力ヘッダーを読み込んでいます。

main関数が実行されると、最初にMAX_CONNECTIONSとactive_connectionsが宣言されます。

記述内容
const int MAX_CONNECTIONS = 128;最大接続数を128で初期化し、書き換えを禁止する
int active_connections = 96;現在の接続数を96で初期化する
active_connections = 100;現在の接続数を100に変更する
MAX_CONNECTIONS = 160;constを付けた値を変更しようとするためエラーになる
return 0;プログラムが正常に終了したことを表す

MAX_CONNECTIONSにはconstが付いているため、初期値の128から別の値へ変更できません。一方、active_connectionsは通常の変数なので、接続状況に合わせて値を変更できます。

最初のprintfでは、最大接続数として128件を表示します。次のprintfでは、現在の接続数として96件を表示します。

接続可能な残り件数は、次の計算で求めています。

MAX_CONNECTIONS - active_connections

MAX_CONNECTIONSの128からactive_connectionsの96を引くため、結果は32になります。

constを付けたMAX_CONNECTIONSも、式の中で値を読み取ることはできます。constによって禁止されるのは書き換えであり、計算への利用ではありません。

その後、次の代入によってactive_connectionsを100に変更しています。

active_connections = 100;

active_connectionsは通常の変数なので、この代入は問題なく実行できます。更新後のprintfでは、変更された100が表示されます。

プログラムの後半にあるMAX_CONNECTIONSへの代入は、コメントにしてあります。

/* MAX_CONNECTIONS = 160; */

このコメントを外すと、constを付けたMAX_CONNECTIONSを書き換えようとするため、コンパイラが問題を検出します。

表示される診断メッセージはコンパイラによって異なりますが、constで修飾された値には代入できないことが示されます。

constの基本ルール

項目内容
constの役割初期化後の書き換えを禁止する
値の読み取り計算、比較、表示などで利用できる
値の書き換え別の値を代入しようとするとコンパイラが問題を検出する
初期化宣言と同時に初期値を設定するのが基本
変数名大文字にすることが多いが必須ではない
適している値プログラムの途中で変更する必要がない値

constを付けても、値を読み取れなくなるわけではありません。計算式、条件式、printfによる表示などでは、通常の変数と同じように利用できます。

禁止されるのは、初期化後に別の値を書き込む操作です。

また、変数名を大文字にすることは読みやすさを高めるための慣習です。大文字で書いただけでは値の変更は禁止されません。実際に書き換えを禁止する働きはconstが担当します。

constを付けた変数と定数の違い

constを付けた変数は、初期化後に値を変更できない変数です。そのため、学習教材や実際の開発ではconst定数と呼ばれることがあります。

ただし、const定数という呼び方は、C言語の標準で定義された正式な用語ではありません。

より正確には、constで修飾されたオブジェクト、またはconstを付けた読み取り専用の変数と考えます。

初心者の段階では、次のように整理しておくとよいでしょう。

  • 通常の変数は後から値を変更できる
  • constを付けた変数は初期化後に値を変更できない
  • 値を変更できないため、定数のような値として利用できる

constは、変数をなくしたり、保存されている値を利用できなくしたりする機能ではありません。

変数として名前と型を持ちながら、初期化後の書き換えだけを禁止する仕組みです。

constに向いている値

値の種類constに向いている理由
計算で使用する固定値税率、円周率、変換係数誤って変更すると計算結果全体に影響する
処理の上限値最大接続数、最大利用者数、最大文字数配列や処理の前提を明確にできる
仕様で固定された値1週間の日数、通信回線数数値の意味を変数名で表現できる
初期設定の基準値警告の基準値、標準待ち時間判定や計算の目的を読み取りやすくできる

これらの値に共通しているのは、プログラムの実行中に変更する必要がないことです。

たとえば、現在の接続数は利用状況によって変化するため、通常の変数が適しています。一方、システムの仕様で決められた最大接続数は、処理の途中で変わってはいけないため、constが適しています。

constに向いていない値

処理の途中で変更する必要がある値には、constを付けません。

次のような値は、通常の変数として宣言します。

  • ユーザーが入力する値
  • 現在の接続数
  • 繰り返し処理で使用するカウンター
  • センサーから取得する測定値
  • 計算の途中結果
  • 時間の経過によって変化する値

途中で変更したくなる値にconstを付けると、必要な代入ができなくなります。

その場合はconstを外すだけでなく、本当に固定すべき値と変化する値が混ざっていないかを確認することが大切です。

たとえば、最大接続数と現在の接続数は、次のように別々の変数で管理します。

適した宣言
システムの最大接続数constを付けた変数
現在の接続数通常の変数
1週間の日数constを付けた変数
今日までの経過日数通常の変数
最大利用者数constを付けた変数
現在の利用者数通常の変数

最大値や仕様値は固定し、現在値や測定値は変更できるようにします。この切り分けによって、それぞれの変数の役割が明確になります。

constを使うかどうかの判断方法

constを使うか迷ったときは、宣言した値を初期化後に変更する必要があるかを考えます。

  • 仕様として最後まで固定する値にはconstを付ける
  • ユーザー入力などで変化する値は通常の変数にする
  • 処理の途中で更新する値は通常の変数にする
  • 固定部分と変化する部分がある場合は別々の変数に分ける

変更が必要な値までconstにすると、後から書き換えられず、プログラムの設計が不自然になります。

反対に、固定すべき値を通常の変数にすると、誤って変更される可能性が残ります。

図3:constと通常の変数を選ぶ流れ

この図から分かること

constを付けるかどうかは、値を初期化した後に変更する必要があるかで判断できます。

MAX_CONNECTIONSやTAX_RATEのように、仕様や計算の前提として固定する値にはconstが適しています。

一方、active_connectionsやsensor_valueのように、プログラムの実行中に変化する値は通常の変数にします。

固定値と変化する値を明確に分けると、コードを見ただけでそれぞれの役割を判断しやすくなります。さらに、変更してはいけない値をコンパイラに守ってもらえるため、誤った代入によるバグも防ぎやすくなります。