トラック5・📦変数・型・演算子

定数——const と constexpr

読む目安 約11分・ゴール:constとconstexpr(C23)を使い分け、変わらない値を宣言時に固定できる

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

プログラムには「絶対に変わってほしくない値」があります。消費税率、円周率、1週間の日数。これを普通の変数にしておくと、どこかで書き換わっても気づけません。値に鍵をかけるのがconstです。

const——書き換え禁止の箱

#include <stdio.h>

int main(void) {
    const double ZEIRITSU = 1.10;
    int honntai = 1200;

    printf("税込 %.0f 円\n", honntai * ZEIRITSU);
    // ZEIRITSU = 1.08;   // ここを有効にするとコンパイルエラー
    return 0;
}

const を付けた変数は、初期化のときにだけ値を持てます。あとから代入しようとすると、コンパイルの時点でエラーになって止まります。実行してから気づくはずだったバグが、書いた時点で防げるわけです。

定数名は大文字で書く習慣があります。読む人に「これは変わらない」と一目で伝わります。

constexpr——C23の「コンパイル時に決まる定数」

#include <stdio.h>

int main(void) {
    constexpr int TSUKI = 12;
    constexpr int NENKAN = 365;
    constexpr int HEIKIN = NENKAN / TSUKI;

    printf("1か月あたり約 %d 日\n", HEIKIN);
    return 0;
}

constexprはC23で入った書き方で、「この値はコンパイルの時点で確定している」という宣言です。const との違いは、コンパイラが値そのものを知っていること。そのおかげで、配列の要素数(トラック8)のような「コンパイル時に決まっていなければならない場所」でも使えます。

#define との違い

#include <stdio.h>
#define ZEI_A 1.10                  // 単なる文字の置き換え。型が無い

int main(void) {
    const double ZEI_B = 1.10;      // 型のある定数
    constexpr double ZEI_C = 1.10;  // 型があり、コンパイル時に確定

    printf("%.2f %.2f %.2f\n", ZEI_A, ZEI_B, ZEI_C);
    return 0;
}

#define はコンパイル前に文字を機械的に置き換えるだけの指示です(トラック16で詳しくやります)。型のチェックが効かず、エラーメッセージも読みにくくなります。C23ではまず constexpr、次に const、#define は最後の手段と考えておけば十分です。

師範変わらぬと分かっているものに、先に鍵をかけておく。それだけで、後日の下手人探しが半日減るのじゃ。

罠・注意

const は宣言と同時に初期化する。 const int N; N = 5; はエラーです。鍵をかけてから中身を入れることはできません。

constexpr の右辺は定数式だけ。 scanf で読んだ値のように、実行時にしか決まらないものは入れられません。

古い環境では constexpr が通らない。 C23対応のコンパイラが必要です(gcc 13以降など)。通らない環境では const で代用します。

まとめ

const は書き換え禁止の変数、constexpr(C23)はコンパイル時に値が確定する定数です。どちらも「変わらないもの」を宣言時に明示して、事故を書いた時点で止めるための道具。型のない #define より、型のある定数を優先します。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

const と constexpr(C23)の違いとして正しいものはどれですか。

  1. const は値を書き換えられるが、constexpr は書き換えられない
  2. constexpr はコンパイル時に値が確定していて、配列の要素数などにも使える
  3. constexpr の値は実行時に決まるので、scanf で読んだ値も入れられる
  4. 2つはまったく同じ意味で、書き方が違うだけ
答えを見る

正解:B(constexpr はコンパイル時に値が確定していて、配列の要素数などにも使える)

どちらも書き換えは禁止ですが、constexprは「コンパイルの時点で値が確定している」ことまで保証します。そのためコンパイル時に決まっていなければならない場所(配列の要素数など)でも使えます。逆に、実行時にしか決まらない値は constexpr には入れられません。

確認問題 2

コンパイルすると error: assignment of read-only variable 'ZEI' と出ました。バグのある行はどれですか。

1#include <stdio.h>
2
3int main(void) {
4    const double ZEI = 1.10;
5    ZEI = 1.08;
6    printf("%.2f\n", ZEI);
7    return 0;
8}
答えを見る

バグのある行:5行目

5行目でconstの変数に代入しています。const 付きの変数は初期化のときにしか値を持てません。税率を8%にしたいなら4行目の初期値を 1.08 に変えます。実行中に変える必要があるなら、そもそも const を付けません。

read-only variable(読み取り専用の変数)は「鍵のかかった箱に入れようとした」という報告です。const の値打ちは、まさにこの誤りを実行前に止めてくれるところにあります。変わらないと分かっている値には、最初から鍵をかけておきます。

「変数・型・演算子」の目次

  1. 変数とは名前付きの箱
  2. int型と整数の世界
  3. 浮動小数点数 float / double
  4. char型と文字コード
  5. bool型と真偽——C23標準の書き方
  6. 定数——const と constexpr
  7. 算術演算子と優先順位
  8. 整数の割り算と剰余の罠
  9. 型変換——暗黙と明示
  10. 2進数・16進数リテラル(0b / 0x / 桁区切り')
  11. sizeofとメモリ上のサイズ
  12. オーバーフローという事件

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版