C言語基礎|標準規格と標準C

“方言C”で迷子にならない!標準規格(ISO/ANSI/JIS)と標準Cの流れを、今日ここでスッキリ整理しよう。

C言語って、同じCのはずなのに「環境によって動いたり動かなかったり」することがありますよね。
それ、だいたい 標準規格に対する解釈の差独自拡張(方言) が原因です。

C言語が広まるほど、いろんなコンパイラや環境が増えて、便利な独自機能も増えました。
その一方で「別の環境に移植したらビルドが通らない…」みたいなことも起きやすくなります。

そこで重要になるのが 標準規格(Standard)
この記事では、ISO/ANSI/JIS といった標準化の枠組みと、C89/C99/C11/C17…という“標準C”の世代を、表や図でしっかりつかめるように解説します。

そもそも「標準規格」って何?

標準規格は、ざっくり言うと 「C言語はこう振る舞う」っていう世界共通のルールブックです。
ルールがあるから、移植性(ポータビリティ)を高く保てます。

表:標準規格があると嬉しい理由

観点標準規格がないと…標準規格があると…
移植性環境ごとに書き換えが必要同じコードが動きやすい
学習本や資料が環境依存になりがち共通ルールで学べる
品質“たまたま動く”が増える期待する動作を言語化できる
チーム開発人によって前提がズレる合意点(ルール)を作れる

表の説明

  • 「標準に沿っているか?」が、コードの信頼性・移植性の土台になります。
  • 逆に“方言に依存した便利機能”は、長期運用や移植で困りやすいです。

ISO / ANSI / JIS の関係

本文に出てきた組織名、ここで整理しましょう。

標準化のイメージ(ざっくり)

(米国国内規格)ANSI   ──┐
                           ├─→(国際規格)ISO/IEC 9899(Cの国際標準)
(各国の国内規格)JIS  ──┘

図の説明

  • ANSI は米国の標準化団体、ISO は国際標準化機構です。
  • 実務上は「ANSIが先にまとまった → ほぼ同内容でISO化 → 国内規格(JIS)にも反映」という流れで語られることが多いです。
  • だから日本では慣習的に ANSI C と呼ばれることがありましたが、世界標準としては「標準C(ISO C)」と捉えるのが自然です。

C89 / C90 / C99 / C11 / C17 って何?

C言語の標準規格は、何度も改訂されてきました。一般に「制定(または確定)年」で呼ばれます。

代表的な“標準C”の世代(覚え方つき)

呼び名だいたいの意味目安になる特徴(超ざっくり)
C89 / C90最初の本格的標準(ANSI→ISO)以後の基準になる“土台”
C991990年代の大改訂便利な機能がいろいろ増える世代
C112010年代の改訂並行性/ライブラリ面など強化
C17C11の小改訂(主に不具合修正寄り)大きな言語追加は少なめ
C232020年代の改訂新しめの整理・追加(環境対応も)

表の説明

  • C17 は「仕様が2017年に固まった」という言い方がされ、出版年の都合で C18 と呼ぶ人もいます。中身は同じものを指すことが多いです。
  • 現場では「自分のコンパイラがどの世代の標準をターゲットにしているか」が超重要になります。

「標準に準拠する」とはどういうこと?

ここ、ふわっとしがちなので言葉を揃えます。

よく出る用語の意味

用語意味例(イメージ)
標準規格仕様書そのもの(ルール)ISO/IEC 9899
標準Cその規格に沿ったC言語C11準拠のC
処理系(実装)コンパイラ+ライブラリなどgcc + 標準ライブラリ
拡張規格外の便利機能コンパイラ独自の構文など
可搬性(移植性)環境を変えても動きやすい性質OSやCPUが違っても通る

表の説明

  • “標準Cで書く”とは、できるだけ規格に沿った書き方をすること。
  • “拡張”は便利だけど、移植性の敵になりやすいので、使うなら意識して使うのがコツです。

自分の環境は「どの標準C」なの?(サンプルプログラム)

「今のコンパイラが標準Cのどの世代を想定しているか」を、マクロでざっくり表示するプログラムです。

プロジェクト名:chap14-2-1 ソースファイル名:chap14-2-1.c

#include <stdio.h>

int main(void)
{
    /* C標準への準拠を示すマクロ(C89から) */
    #ifdef __STDC__
        printf("この処理系は、標準Cとして動作する設定です。\n");
    #else
        printf("この処理系は、標準Cとしての動作が保証されません。\n");
    #endif

    /* Cの世代を表す値(多くの処理系で定義される) */
    #ifdef __STDC_VERSION__
        printf("標準Cの世代を示す値(__STDC_VERSION__)は %ld です。\n", (long)__STDC_VERSION__);

        /* よくある値を目安として判定する(環境により差があり得ます) */
        if (__STDC_VERSION__ >= 202311L) {
            printf("目安:C23 相当のモードです。\n");
        } else if (__STDC_VERSION__ >= 201710L) {
            printf("目安:C17 相当のモードです。\n");
        } else if (__STDC_VERSION__ >= 201112L) {
            printf("目安:C11 相当のモードです。\n");
        } else if (__STDC_VERSION__ >= 199901L) {
            printf("目安:C99 相当のモードです。\n");
        } else if (__STDC_VERSION__ >= 199409L) {
            printf("目安:C95(C90の修正版)相当のモードです。\n");
        } else {
            printf("目安:C90 相当のモードです。\n");
        }
    #else
        printf("__STDC_VERSION__ が未定義です。目安:C90(または独自設定)相当の可能性があります。\n");
    #endif

    return 0;
}

このプログラムで登場する項目の解説

図:マクロで“世代”を知る流れ

コンパイラのモード(例:C11など)
        ↓
定義されるマクロ(__STDC__ / __STDC_VERSION__)
        ↓
プログラムが表示する(今の標準Cの目安)

図の説明

  • 「どの標準をターゲットにしてコンパイルしているか」で、定義されるマクロが変わります。
  • つまり、コードの移植性を考えるなら「どの標準を前提にするか」を、先に決めるのが大事です。

重要:標準が違うと何が困る?

標準が違うと、文法・ライブラリ・未定義動作の扱いなどの“前提”がズレます。

標準差分で起きがちなトラブル例

トラブルよくある原因対策の方向性
ある環境ではビルドできるのに、別環境で失敗その機能が標準外 / 世代違いターゲット標準を決める
同じコードなのに結果が違う未定義動作・処理系定義動作規格に沿って安全に書く
警告が大量に出る書式や型が標準とズレる警告をエラー扱いにして潰す

表の説明

  • “コンパイルが通る”は最低ラインで、標準準拠の意識があると品質が安定します。
  • 未定義動作は特に怖いので、「標準が決めていない動き」を踏まないのがコツです。

現場でのおすすめの考え方

  • チームや教材は「この標準で書く」を先に宣言(例:C11で統一)
  • 拡張は使うなら明示的に(理由・範囲・代替を残す)
  • ポータブルにしたい場所ほど標準に寄せる