C言語/C言語の変遷

出典: フリー教科書『ウィキブックス(Wikibooks)』

この章では、プログラミング言語Cの歴史をたどり、ISO規格の進化や『プログラミング言語C』の各版の寄与を明らかにします。初期のCから現代の標準まで、言語機能や設計思想の変遷を探り、読者に深い理解を提供します。

C言語は、1972年にデニス・リッチーとケン・トンプソンによってベル研究所で開発されました。以後、C言語は数十年にわたり進化し、標準化されてきました。以下はC言語の主な仕様の変遷です。

The C Programming Language 第一版 (1972)[編集]

最初のC言語仕様で、ブライアン・カーニハンとデニス・リッチーによる書籍 "The C Programming Language"(通称K&R C;邦題『プログラミング言語C』)の初版 で紹介されました。このバージョンは標準化されていませんでしたが、Cの基本的な特徴が確立されました。

  1. 標準ライブラリの限定: K&R Cの初期のバージョンでは、標準ライブラリが非常に限定されていました。基本的な入出力関数やメモリ管理関連の関数が提供されていましたが、後のバージョンと比較して機能が制限されていました。
  2. 基本的なデータ型: K&R Cは、基本的なデータ型(int、char、floatなど)をサポートしており、これらの型を使用して変数や関数を宣言できました。ただし、後のバージョンとは異なり、標準のvoid型が存在していませんでした。
  3. 制御構造: K&R Cは、基本的な制御構造(if、while、forなど)をサポートしていました。制御構造の文法は、後のバージョンと類似していますが、いくつかの微細な差異がありました。
  4. ポインタとアドレス演算子: ポインタ機能は早い段階から存在しており、アドレス演算子 & と間接参照演算子 * を使用してメモリのアドレスやその値にアクセスできました。ポインタの扱いは後のバージョンと同様に重要でした。
  5. 構造体: K&R Cでは、構造体が導入され、ユーザーが複数の異なるデータ型をまとめるために使用できました。これにより、複雑なデータ構造を作成する能力が向上しました。

K&R CはC言語の初期の形態を示しており、その後の標準化プロセスでいくつかの変更が行われています。後のバージョンでは、機能の追加や改善が行われ、より広範なプログラミングニーズに対応するようになりました。

ANSI C (C89 / C90):ISO/IEC 9899:1990[編集]

1989年に米国標準協会(ANSI)によって標準化されました。これにより、C言語は初めて公式に標準仕様を持つこととなりました。このバージョンはISOでも標準化され、C89またはC90として知られています。

  1. ANSI Cでは、関数のプロトタイプ(関数の宣言)が導入されました。これは、関数の引数の型と数を宣言するためのものであり、関数定義よりも前にプロトタイプを宣言することが推奨されます。K&R Cでは引数の型が省略されていましたが、ANSI Cでは型が指定されるようになりました。
    // K&R C
    int add(a, b)
        int a, b;
    {
        return a + b;
    }
    
    // ANSI C
    int add(int a, int b)
    {
        return a + b;
    }
    
  2. 新しいデータ型: ANSI Cでは新しいデータ型が導入されました。例えば、void型やlong double型などが追加されました。これらの新しい型がコード内で使用されている場合、適切に修正する必要があります。
  3. 新しいキーワード: ANSI Cでは新しいキーワードが導入されました。これにはconstvolatilesignedunsignedなどが含まれます。これらのキーワードは変数の修飾や型指定に使用されます。
  4. ヘッダーとライブラリ: ANSI Cでは新しいヘッダーやライブラリが導入され、標準ライブラリが拡張されました。コード内で新しい関数やヘッダーを使用している場合、これらを正しく取り込む必要があります。
  5. 可変引数関数: ANSI Cでは、可変引数関数の宣言や呼び出しにおいて、<stdarg.h>ヘッダーが必要です。これは、va_startva_argva_endなどの関数が含まれています。

C99:ISO/IEC 9899:1999[編集]

1999年にISOで標準化されたCの改訂版。新しい機能が導入され、標準ライブラリが拡張されました。代表的な変更に、可変長引数関数、複数の宣言を1つの文で行えるようにするブロック内の変数宣言などがあります。

C90からC99への移行では、いくつかの新しい機能が導入されているため、以下はその注意点です。

  1. 可変長配列
    C99では、可変長配列(Variable Length Array, VLA)が導入されました。これは、実行時にサイズが決まる配列で、動的にメモリを確保する必要がない点が異なります。自動変数なのでスコープは定義されたブロック(通常は関数)で、確保されるのはスタック上なので、特に再帰を行うような場合はスタックオーバーフローに注意が必要です。
    // C99
    void processArray(int size) {
        int myArray[size];  // 可変長配列
        // 配列の処理
    }
    
  1. 複合リテラル
    複合リテラル(Compound Literals)は、任意の型の一時的なオブジェクトを作成するための機能で、リテラルの一種です。この機能により、式の結果として生成された一時的な複合型のオブジェクトに対して、直接リテラルで初期化を行うことができます。
    // C99以降
    // (type-name) { initializer-list }
    
    // 例
    int main() {
        // int型の複合リテラル
        int array[] = {1, 2, 3};
        int *ptr = (int[]){4, 5, 6};
    
        // 構造体型の複合リテラル
        struct Point {
            int x;
            int y;
        };
        struct Point p = (struct Point){.x = 10, .y = 20};
    
        return 0;
    }
    
    この例では、int型と構造体型の複合リテラルを使用しています。複合リテラルは、配列や構造体の初期化、動的メモリ割り当てなどで利用されることがあります。また、ポインタを介して複合リテラルで初期化されたオブジェクトにアクセスすることも可能です。
  2. inline関数
    C99では、inlineキーワードが導入され、関数をインライン展開する指示をすることができます。これにより、関数呼び出しのオーバーヘッドを減らすことができます。
    // C99
    inline int add(int a, int b) {
        return a + b;
    }
    
  3. //による行コメント
    C99では、//を使った行コメントが導入されました。これは、C90では使えなかった機能です。
    // C99 行コメント
    
  4. 柔軟な配列初期化
    C99では、柔軟な配列初期化がサポートされています。これにより、配列の一部分だけを初期化することが可能となりました。
  5. stdint.hヘッダー
    C99では、整数型の厳密な幅を指定するための<stdint.h>ヘッダーが導入されました。これにより、プログラムの可搬性を向上させることができます。
    // C99
    int myArray[] = {1, 2, [5] = 10, 20};
    
  6. ブロックスコープの変数の宣言
    C99では、ブロック内での変数宣言が、ブロックの先頭ではなく、どこでもできるようになりました。
    // C99
    #include <stdint.h>
    int32_t myInt = 42;
    

C11:ISO/IEC 9899:2011[編集]

2011年にISOで標準化されたC言語の次のバージョン。新しい機能や改良が導入され、マルチスレッドプログラミングをサポートするスレッドサポートライブラリ(Thread Support Library)が導入されました。

C99からC11への移行は、C言語の新しい標準であるC11で導入された機能や変更を考慮する必要があります。以下は、C99からC11への移行の注意点のいくつかです:

  1. 新しいキーワードと機能: C11では、新しいキーワードや機能が導入されています。例えば、_Alignas_Alignof_Noreturnなどがあります。これらのキーワードや機能がコード内で使用されている場合、適切に修正する必要があります。
  2. _Static_assertの導入: C11では、静的アサートを行うための_Static_assertが導入されました。これは、コンパイル時に条件が成り立っているか確認するために使用されます。
    // C11
    _Static_assert(sizeof(int) == 4, "int must be 4 bytes");
    
  3. _Genericキーワードの拡張
    C11では、_Genericキーワードが拡張され、ジェネリックなマクロの定義がサポートされています。これにより、異なるデータ型に対する汎用的なコードを記述することが容易になります。
    // C11
    #define print_value(x) _Generic((x), int: printf("%d\n", x), double: printf("%f\n", x))
    
    int main() {
        print_value(42);    // int
        print_value(3.14);  // double
        return 0;
    }
    
  4. アトミック型
    C11では、アトミック操作を行うためのアトミック型(atomic types)が導入されています。これにより、マルチスレッド環境での競合状態を回避するための機能が強化されました。
    // C11
    #include <stdatomic.h>
    _Atomic int counter = ATOMIC_VAR_INIT(0);
    atomic_fetch_add(&counter, 1);
    
  5. Unicodeサポートの向上
    C11では、Unicodeサポートが向上し、新しいエスケープシーケンスが導入されました。これにより、Unicode文字やエスケープシーケンスをより直感的に扱うことができます。
    // C11
    char japanese[] = u8"日本語";  // UTF-8エンコード
    

C2x:ISO/IEC DIS 9899:2023[編集]

策定中の ISO/IEC 9899:2023(通称C2xまたはC23)での現在知りうる変更点の一部。

  1. プロトタイプ宣言が必須化:
    • 識別子リストによる関数定義のサポートを削除し、プロトタイプ宣言が必須化されました。
  2. 空の引数リストの意味がC++と同じに:
    • 関数宣言の引数リストが空の場合、voidを1つだけ含む引数リストと同じように扱うことが義務付けられました。
  3. ISO/IEC 9945 (POSIX)との整合性の向上:
    • strftime関数に拡張月名フォーマットが追加されました。
    • いくつかの関数(gmtime_r, localtime_r, memccpy, strdup, strndup)が統合されました。
  4. 浮動小数点規格IEC 60559との整合性の向上:
    • バイナリ浮動小数点技術仕様 TS 18661-1、10進浮動小数点技術仕様書 TS 18661-2、数学関数技術仕様書 TS 18661-4a の統合が行われました。
    • DECIMAL_DIG マクロが廃止されました。
  5. キーワードに昇格したマクロ
    • bool, static_assert, true, false, thread_local がマクロからキーワードに昇格しました。
    • 新旧のキーワードをマクロとして定義することで、プログラムの移行を容易にすることができるようになりました。
  6. 旧式の関数定義の廃止
    • ANSI Cのプロトタイプ宣言以前の、K&R1形式の関数宣言が廃止されました。
  7. トリグラフの削除
    • #??= の3文字で表すトリグラフ(JIS語では「3文字表記」)が規格から削除されました。
  8. 符号付き整数の形式は二の補数に限定
    • 整数の符号表現が二の補数に限定され、他の符号表現の仕様が削除されました。
  9. 二進数リテラルの追加
    • 二進数リテラルが導入され、接頭辞 0b または 0B の後に0または1の数字が連続したものが使用できるようになりました。
  10. 二進整数の書式付き入出力
    • 二進数リテラルに対応する形で、printf系とscanf系の関数に書式 %b が追加されました。
  11. static_assertの一引数版:
    • static_assert の第二引数がオプショナルになりました。
  12. 属性の追加
    • 属性(attribute)に関する構文と種類が規格化され、開発者が言語エンティティに追加の情報を付加できるようになりました。
  13. 型推論の導入
    • オブジェクト定義における型推論が規格化され、auto キーワードを使用して初期化子から変数の型を推論できるようになりました。
  14. 可変モディファイ型のサポートの義務化
    • 可変モディファイ型のサポートが義務付けられました(ただし、可変長配列そのものではありません)。
  15. 関数の引数の省略記号の使用可能化
    • 関数定義の引数の省略記号 ... が、関数のパラメーターリストにおいて先行するパラメータなしで使用できるようになりました。
  16. 複合リテラルのライフタイムの変更
    • 複合リテラルには、型の一部としてストレージクラス指定子を含めることができ、複合リテラルのライフタイムを変更することが可能になりました。
  17. ライブラリーヘッダーにバージョンテスト用マクロを追加:
    • ライブラリヘッダーに、アップグレードや移植を支援するためのバージョンテスト用マクロが追加されました。
  18. ラベル配置可能位置の拡大:
    • 宣言の前や複合文の末尾にラベルを配置できるようになりました。
  19. 属性を含むアトリビュート機能を追加:
    • deprecated, fallthrough, maybe_unused, nodiscard, reproducible, unsequenced, noreturn などの属性を含むアトリビュート機能が追加されました。
  20. u8文字プリフィックス:
    • u8文字列プリフィックスが追加されました。
  21. u8, u, U文字列のエンコードの規格化:
    • u8, u, Uをプリフィックスとする文字列を、それぞれ ISO/IEC 10646 で定義された UTF-8, UTF-16, UTF-32 とすることが義務付けられました。
  22. エンコーディングの分離:
    • 文字列と文字のためのリテラル、ワイドリテラル、UTF-8リテラル、UTF-16リテラル、UTF-32リテラルエンコーディングが分離され、それぞれの実行ベースバージョンが単独で持つようになりました。
  23. uchar.hに関数を追加:
    • ヘッダー <uchar.h> に欠落していた mbrtoc8c8rtomb 関数が追加されました。
  24. constexpr:
    • オブジェクト定義に constexpr 指定子を追加し、constexpr ストレージクラス指定子と組み合わせて定数式として認識される内容が改善されました。
  25. typeofとtypeof_unqual:
    • 式の型を推論する typeoftypeof_unqual 演算子が追加されました。
  26. タグの互換性ルールの改善:
    • タグの互換性ルールに変更が加わり、既存の矛盾を解消し、マクロを用いた汎用的なプログラミングを容易にし、代数的な型の構築を可能にすることができるようになりました。
  27. ビット精度の整数型 _BitInt の追加:
    • ビット精度の整数型 _BitInt が追加されました。
  28. enumの基礎となる型が指定可能に:
    • 列挙型に基礎となる型を指定する機能が追加されました。
  29. QCharやQVoidの導入:
    • 既存関数の宣言が変更され、関数内に配置された型の定数性(const-ness)を保持するようになりました。
  30. #embed:
    • プリプロセッサディレクティブ #embed が追加され、バイナリーデータを可能な限り忠実に埋め込む機能が提供されました。
  31. nullptr定数とnullptr_t型を追加:
    • nullptr 定数と nullptr_t 型が追加されました。
  32. VA_OPT:
    • __VA_OPT__ は、マクロのパラメーターとして受け取った可変引数が空でない場合に置換するトークンを指定します。
  33. 可変モディファイ型のサポートが義務化:
    • 可変モディファイ型(Variably-Modified Types)のサポートが義務付けられました。
  34. Unicode 識別子更新:
    • Unicode 識別子が、UAX(Unicode Standard Annex) #31 に従うようになりました。

C言語のキーワードの変遷[編集]

C言語のキーワードは、言語の進化や新しい機能の導入に伴って変わってきました。以下に、主なC言語の標準ごとのキーワードの変遷を示します。ただし、すべてのキーワードや変更点を網羅するわけではありません。

  1. K&R C (1972年): 最初のC言語仕様では、基本的なキーワードのセットが導入されました。代表的なものとして、intcharforifwhileなどがあります。
  2. ANSI C (C89/C90、1989年): ANSI Cの導入に伴い、新しいキーワードが追加されました。代表的なものとして、voidconstvolatileなどが挙げられます。
  3. ISO/IEC C99 (1999年): C99では、新しいキーワードが導入されました。代表的なものに、inline_Boolrestrict_Complex_Imaginaryなどがあります。
  4. ISO/IEC C11 (2011年): C11では、新しいキーワードが導入され、様々な機能が拡張されました。代表的なものに、_Alignas_Alignof_Atomic_Noreturn_Static_assertなどがあります。
  5. 将来のバージョン: 将来のC言語のバージョンでは、新しい機能や拡張が導入され、それに伴い新しいキーワードが追加される可能性があります。新しい標準の策定プロセスで提案された機能が取り込まれることで、言語の進化が続きます。

C言語のキーワードの変遷は、言語の進化によって柔軟性や表現力が向上しています。新しいキーワードを使用することで、より効率的なコーディングや新しいプログラミングスタイルを採用することができます。


脚註[編集]