C言語/C言語の変遷
この章では、プログラミング言語Cの歴史をたどり、ISO規格の進化や『プログラミング言語C』の各版の寄与を明らかにします。初期のCから現代の標準まで、言語機能や設計思想の変遷を探り、読者に深い理解を提供します。
C言語は、1972年にデニス・リッチーとケン・トンプソンによってベル研究所で開発されました。以後、C言語は数十年にわたり進化し、標準化されてきました。以下はC言語の主な仕様の変遷です。
The C Programming Language 第一版 (1972)
[編集]最初のC言語仕様で、ブライアン・カーニハンとデニス・リッチーによる書籍 "The C Programming Language"(通称K&R C;邦題『プログラミング言語C』)の初版 で紹介されました。このバージョンは標準化されていませんでしたが、Cの基本的な特徴が確立されました。
- 標準ライブラリの限定: K&R Cの初期のバージョンでは、標準ライブラリが非常に限定されていました。基本的な入出力関数やメモリ管理関連の関数が提供されていましたが、後のバージョンと比較して機能が制限されていました。
- 基本的なデータ型: K&R Cは、基本的なデータ型(int、char、floatなど)をサポートしており、これらの型を使用して変数や関数を宣言できました。ただし、後のバージョンとは異なり、標準の
void
型が存在していませんでした。 - 制御構造: K&R Cは、基本的な制御構造(if、while、forなど)をサポートしていました。制御構造の文法は、後のバージョンと類似していますが、いくつかの微細な差異がありました。
- ポインタとアドレス演算子: ポインタ機能は早い段階から存在しており、アドレス演算子
&
と間接参照演算子*
を使用してメモリのアドレスやその値にアクセスできました。ポインタの扱いは後のバージョンと同様に重要でした。 - 構造体: K&R Cでは、構造体が導入され、ユーザーが複数の異なるデータ型をまとめるために使用できました。これにより、複雑なデータ構造を作成する能力が向上しました。
K&R CはC言語の初期の形態を示しており、その後の標準化プロセスでいくつかの変更が行われています。後のバージョンでは、機能の追加や改善が行われ、より広範なプログラミングニーズに対応するようになりました。
ANSI C (C89 / C90):ISO/IEC 9899:1990
[編集]1989年に米国標準協会(ANSI)によって標準化されました。これにより、C言語は初めて公式に標準仕様を持つこととなりました。このバージョンはISOでも標準化され、C89またはC90として知られています。
- 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; }
- 新しいデータ型: ANSI Cでは新しいデータ型が導入されました。例えば、
void
型やlong double
型などが追加されました。これらの新しい型がコード内で使用されている場合、適切に修正する必要があります。 - 新しいキーワード: ANSI Cでは新しいキーワードが導入されました。これには
const
、volatile
、signed
、unsigned
などが含まれます。これらのキーワードは変数の修飾や型指定に使用されます。 - ヘッダーとライブラリ: ANSI Cでは新しいヘッダーやライブラリが導入され、標準ライブラリが拡張されました。コード内で新しい関数やヘッダーを使用している場合、これらを正しく取り込む必要があります。
- 可変引数関数: ANSI Cでは、可変引数関数の宣言や呼び出しにおいて、
<stdarg.h>
ヘッダーが必要です。これは、va_start
、va_arg
、va_end
などの関数が含まれています。
C99:ISO/IEC 9899:1999
[編集]1999年にISOで標準化されたCの改訂版。新しい機能が導入され、標準ライブラリが拡張されました。代表的な変更に、可変長引数関数、複数の宣言を1つの文で行えるようにするブロック内の変数宣言などがあります。
C90からC99への移行では、いくつかの新しい機能が導入されているため、以下はその注意点です。
- 可変長配列
- C99では、可変長配列(Variable Length Array, VLA)が導入されました。これは、実行時にサイズが決まる配列で、動的にメモリを確保する必要がない点が異なります。自動変数なのでスコープは定義されたブロック(通常は関数)で、確保されるのはスタック上なので、特に再帰を行うような場合はスタックオーバーフローに注意が必要です。
// C99 void processArray(int size) { int myArray[size]; // 可変長配列 // 配列の処理 }
- 複合リテラル
- 複合リテラル(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
型と構造体型の複合リテラルを使用しています。複合リテラルは、配列や構造体の初期化、動的メモリ割り当てなどで利用されることがあります。また、ポインタを介して複合リテラルで初期化されたオブジェクトにアクセスすることも可能です。
inline
関数- C99では、
inline
キーワードが導入され、関数をインライン展開する指示をすることができます。これにより、関数呼び出しのオーバーヘッドを減らすことができます。 // C99 inline int add(int a, int b) { return a + b; }
- C99では、
//
による行コメント- C99では、
//
を使った行コメントが導入されました。これは、C90では使えなかった機能です。 // C99 行コメント
- C99では、
- 柔軟な配列初期化
- C99では、柔軟な配列初期化がサポートされています。これにより、配列の一部分だけを初期化することが可能となりました。
stdint.h
ヘッダー- C99では、整数型の厳密な幅を指定するための
<stdint.h>
ヘッダーが導入されました。これにより、プログラムの可搬性を向上させることができます。 // C99 int myArray[] = {1, 2, [5] = 10, 20};
- C99では、整数型の厳密な幅を指定するための
- ブロックスコープの変数の宣言
- 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への移行の注意点のいくつかです:
- 新しいキーワードと機能: C11では、新しいキーワードや機能が導入されています。例えば、
_Alignas
や_Alignof
、_Noreturn
などがあります。これらのキーワードや機能がコード内で使用されている場合、適切に修正する必要があります。 _Static_assert
の導入: C11では、静的アサートを行うための_Static_assert
が導入されました。これは、コンパイル時に条件が成り立っているか確認するために使用されます。// C11 _Static_assert(sizeof(int) == 4, "int must be 4 bytes");
_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; }
- C11では、
- アトミック型
- C11では、アトミック操作を行うためのアトミック型(atomic types)が導入されています。これにより、マルチスレッド環境での競合状態を回避するための機能が強化されました。
// C11 #include <stdatomic.h> _Atomic int counter = ATOMIC_VAR_INIT(0); atomic_fetch_add(&counter, 1);
- Unicodeサポートの向上
- C11では、Unicodeサポートが向上し、新しいエスケープシーケンスが導入されました。これにより、Unicode文字やエスケープシーケンスをより直感的に扱うことができます。
// C11 char japanese[] = u8"日本語"; // UTF-8エンコード
C2x:ISO/IEC DIS 9899:2023
[編集]策定中の ISO/IEC 9899:2023(通称C2xまたはC23)での現在知りうる変更点の一部。
- プロトタイプ宣言が必須化:
- 識別子リストによる関数定義のサポートを削除し、プロトタイプ宣言が必須化されました。
- 空の引数リストの意味がC++と同じに:
- 関数宣言の引数リストが空の場合、
void
を1つだけ含む引数リストと同じように扱うことが義務付けられました。
- 関数宣言の引数リストが空の場合、
- ISO/IEC 9945 (POSIX)との整合性の向上:
strftime
関数に拡張月名フォーマットが追加されました。- いくつかの関数(
gmtime_r
,localtime_r
,memccpy
,strdup
,strndup
)が統合されました。
- 浮動小数点規格IEC 60559との整合性の向上:
- バイナリ浮動小数点技術仕様 TS 18661-1、10進浮動小数点技術仕様書 TS 18661-2、数学関数技術仕様書 TS 18661-4a の統合が行われました。
DECIMAL_DIG
マクロが廃止されました。
- キーワードに昇格したマクロ
bool
,static_assert
,true
,false
,thread_local
がマクロからキーワードに昇格しました。- 新旧のキーワードをマクロとして定義することで、プログラムの移行を容易にすることができるようになりました。
- 旧式の関数定義の廃止
- ANSI Cのプロトタイプ宣言以前の、K&R1形式の関数宣言が廃止されました。
- トリグラフの削除
#
を??=
の3文字で表すトリグラフ(JIS語では「3文字表記」)が規格から削除されました。
- 符号付き整数の形式は二の補数に限定
- 整数の符号表現が二の補数に限定され、他の符号表現の仕様が削除されました。
- 二進数リテラルの追加
- 二進数リテラルが導入され、接頭辞
0b
または0B
の後に0または1の数字が連続したものが使用できるようになりました。
- 二進数リテラルが導入され、接頭辞
- 二進整数の書式付き入出力
- 二進数リテラルに対応する形で、printf系とscanf系の関数に書式
%b
が追加されました。
- 二進数リテラルに対応する形で、printf系とscanf系の関数に書式
- static_assertの一引数版:
static_assert
の第二引数がオプショナルになりました。
- 属性の追加
- 属性(attribute)に関する構文と種類が規格化され、開発者が言語エンティティに追加の情報を付加できるようになりました。
- 型推論の導入
- オブジェクト定義における型推論が規格化され、
auto
キーワードを使用して初期化子から変数の型を推論できるようになりました。
- オブジェクト定義における型推論が規格化され、
- 可変モディファイ型のサポートの義務化
- 可変モディファイ型のサポートが義務付けられました(ただし、可変長配列そのものではありません)。
- 関数の引数の省略記号の使用可能化
- 関数定義の引数の省略記号
...
が、関数のパラメーターリストにおいて先行するパラメータなしで使用できるようになりました。
- 関数定義の引数の省略記号
- 複合リテラルのライフタイムの変更
- 複合リテラルには、型の一部としてストレージクラス指定子を含めることができ、複合リテラルのライフタイムを変更することが可能になりました。
- ライブラリーヘッダーにバージョンテスト用マクロを追加:
- ライブラリヘッダーに、アップグレードや移植を支援するためのバージョンテスト用マクロが追加されました。
- ラベル配置可能位置の拡大:
- 宣言の前や複合文の末尾にラベルを配置できるようになりました。
- 属性を含むアトリビュート機能を追加:
deprecated
,fallthrough
,maybe_unused
,nodiscard
,reproducible
,unsequenced
,noreturn
などの属性を含むアトリビュート機能が追加されました。
- u8文字プリフィックス:
u8
文字列プリフィックスが追加されました。
- u8, u, U文字列のエンコードの規格化:
u8
,u
,U
をプリフィックスとする文字列を、それぞれ ISO/IEC 10646 で定義された UTF-8, UTF-16, UTF-32 とすることが義務付けられました。
- エンコーディングの分離:
- 文字列と文字のためのリテラル、ワイドリテラル、UTF-8リテラル、UTF-16リテラル、UTF-32リテラルエンコーディングが分離され、それぞれの実行ベースバージョンが単独で持つようになりました。
- uchar.hに関数を追加:
- ヘッダー
<uchar.h>
に欠落していたmbrtoc8
とc8rtomb
関数が追加されました。
- ヘッダー
- constexpr:
- オブジェクト定義に
constexpr
指定子を追加し、constexpr
ストレージクラス指定子と組み合わせて定数式として認識される内容が改善されました。
- オブジェクト定義に
- typeofとtypeof_unqual:
- 式の型を推論する
typeof
とtypeof_unqual
演算子が追加されました。
- 式の型を推論する
- タグの互換性ルールの改善:
- タグの互換性ルールに変更が加わり、既存の矛盾を解消し、マクロを用いた汎用的なプログラミングを容易にし、代数的な型の構築を可能にすることができるようになりました。
- ビット精度の整数型
_BitInt
の追加:- ビット精度の整数型
_BitInt
が追加されました。
- ビット精度の整数型
- enumの基礎となる型が指定可能に:
- 列挙型に基礎となる型を指定する機能が追加されました。
- QCharやQVoidの導入:
- 既存関数の宣言が変更され、関数内に配置された型の定数性(
const-ness
)を保持するようになりました。
- 既存関数の宣言が変更され、関数内に配置された型の定数性(
- #embed:
- プリプロセッサディレクティブ
#embed
が追加され、バイナリーデータを可能な限り忠実に埋め込む機能が提供されました。
- プリプロセッサディレクティブ
- nullptr定数とnullptr_t型を追加:
nullptr
定数とnullptr_t
型が追加されました。
- VA_OPT:
__VA_OPT__
は、マクロのパラメーターとして受け取った可変引数が空でない場合に置換するトークンを指定します。
- 可変モディファイ型のサポートが義務化:
- 可変モディファイ型(Variably-Modified Types)のサポートが義務付けられました。
- Unicode 識別子更新:
- Unicode 識別子が、UAX(Unicode Standard Annex) #31 に従うようになりました。
C言語のキーワードの変遷
[編集]C言語のキーワードは、言語の進化や新しい機能の導入に伴って変わってきました。以下に、主なC言語の標準ごとのキーワードの変遷を示します。ただし、すべてのキーワードや変更点を網羅するわけではありません。
- K&R C (1972年): 最初のC言語仕様では、基本的なキーワードのセットが導入されました。代表的なものとして、
int
、char
、for
、if
、while
などがあります。 - ANSI C (C89/C90、1989年): ANSI Cの導入に伴い、新しいキーワードが追加されました。代表的なものとして、
void
、const
、volatile
などが挙げられます。 - ISO/IEC C99 (1999年): C99では、新しいキーワードが導入されました。代表的なものに、
inline
、_Bool
、restrict
、_Complex
、_Imaginary
などがあります。 - ISO/IEC C11 (2011年): C11では、新しいキーワードが導入され、様々な機能が拡張されました。代表的なものに、
_Alignas
、_Alignof
、_Atomic
、_Noreturn
、_Static_assert
などがあります。 - 将来のバージョン: 将来のC言語のバージョンでは、新しい機能や拡張が導入され、それに伴い新しいキーワードが追加される可能性があります。新しい標準の策定プロセスで提案された機能が取り込まれることで、言語の進化が続きます。
C言語のキーワードの変遷は、言語の進化によって柔軟性や表現力が向上しています。新しいキーワードを使用することで、より効率的なコーディングや新しいプログラミングスタイルを採用することができます。