C言語/C23の変更点

2023年11月現在の最新のCの国際規格ISO/IEC 9899:2018(通称C17)は、ISO/IEC 9899:2011(通称C11)の不明確な部分を補うErrata的な規格で、実質的にC11が現行の最新規格という状態が10年以上続いています。
このような中、ISO/IEC JTC 1/SC 22 WG14 は、ISO/IEC 9899:2023(通称C2xまたはC23)を2023年11月を目標に策定しています。
ここでは、現在入手可能な working draft — September 3, 2022 ISO/IEC 9899:2023 (E)に基づいて、C23のC11に対する変更点をまとめます。
第5版(2023年)のC言語標準(ISO/IEC 9899:2023)の変更点について、詳細に解説します。
これらの変更は、C言語の標準化において、より使いやすく、柔軟性が高く、規格の一貫性が向上することを目的としています。
C23の変更点のうちでも目立ったものを列挙します。
bool,static_assert,true,false,thread_localがマクロからキーワードに昇格しました。 また、新旧のキーワードをマクロとして定義することで、プログラムの移行を容易にすることができるようになりました。
多くの _ で始まるキーワードがマクロになりましたが、全てではありません。
整数の幅の制約と時代遅れの符号表現(いわゆる「1の補数」と「符号-マグニチュード」)を削除しました。
static_assertの第二引数はオプショナルになりました。
識別子リストによる関数定義のサポートを削除しました。
関数宣言の引数リストが空の場合、voidを1つだけ含む引数リストと同じように扱うことが義務付けられました。
拡張月名フォーマット追加
gmtime_r, localtime_r, memccpy, strdup, strndup を統合。
最高精度の浮動小数点数を有効数字すべてを表現するために必要な 10 進数字の最小桁数を表すマクロ DECIMAL_DIG が廃止されました。DR 501
アップグレードや移植を支援するための変更を含むライブラリヘッダーに、バージョンテスト用マクロが追加されました。 この機能がサポートされていること自身を、まず STDC_VERSION__ マクロで確認する必要があります。
宣言の前や複合文の末尾にラベルを配置できるようにしました。
u8文字列プリフィックスとの整合性の為、u8文字プリフィックスが追加されました。
u8,u,Uをプリフィックスとする文字列を、それぞれ ISO/IEC 10646で定義された UTF-8,UTF-16,UTF-32とすることをが義務付けられました。
文字列と文字のためのリテラル、ワイドリテラル、UTF-8リテラル、UTF-16リテラル、UTF-32リテラルエンコーディングを分離し、これらの実行ベースバージョン、特に実行とワイド実行エンコーディングを単独で持つようになりました。
ヘッダー<uchar.h> に欠落していた mbrtoc8 と c8rtomb 関数が追加されました。
複合リテラル( compound literals )には、型の一部としてストレージクラス指定子を含めることができ、複合リテラルのライフタイムを変更することができます。 複合リテラルのライフタイムを変更する(定数式に変更する場合もある)。
C23のconstexprは、新たに導入された記憶クラス指定子であり、オブジェクトの値をコンパイル時に固定するために使用されます。以下はその主な制約と意味です:
constexprはコンパイル時の計算や値の固定化に役立ちます。
typeof および typeof_unqual は、C言語の新しい演算子です。 これらの演算子は、指定された式や型名に基づいて、型情報を取得するために使用されます。
以下に、それぞれの演算子の概要を説明します:
これらの演算子は、特にマクロの定義や、型を動的に取得する必要がある場合などに役立ちます。 typeof 演算子は C 言語の柔軟性を高め、より効率的なコードを記述するのに役立ちます。
タグの互換性ルールに若干の変更が加わり、既存の矛盾を解消し、マクロを用いた汎用的なプログラミングを容易にし、代数的な型の構築を可能にすることができるようになりました。N2863 N2366 N2105
_BitIntは、ビット精度を指定するための型指定子です。指定されたビット数の整数型を表します。
以下に、_BitIntの使用例を示します。
この例では、_BitIntを使用して8ビット、16ビット、32ビットの整数型を宣言し、それぞれの変数に値を代入しています。_BitIntを使用することで、特定のビット幅を持つ整数型を明示的に指定することができます。
この型の顕著な特性の1つは、性能上の理由から、デフォルトの整数プロモ ーションが行われないことです。 リテラル・サフィックス wb を使用することで、_BitInt とリテラルを含む式の不要な変換をさらに減らすことができます。N2960 N2946 N2775 N2709 N2646
列挙型に基礎となる型を指定する機能が追加されました。 これによって、int よりも大きな範囲の値表現を持つことができるようになりました。
ヘッダー <stdbit.h> が追加されました。 ビットおよびバイトを扱う一連のユーティリティを追加し、多くの実装の最も効率的な機能へポータブルにアクセスできるようにしました。
既存関数の宣言を変更し、関数内に配置された型の定数性( const-ness )を保持するようになりました。N2973
この変更は、Nプロポーザル N2522, 2603およびN2973 で提案されました。
この提案は、C標準ライブラリに含まれる12の検索関数(bsearch bsearch_s memchr strchr strpbrk strrchr strstr wcschr wcspbrk wcsrchr wcsstr wmemchr)が、const修飾子を失うという問題を解決するものです。これらの関数は、検索されるバッファへのconst修飾ポインタを受け取りますが、const修飾を失ったポインタを返します。提案では、これらの関数が、入力バッファの修飾子と同じ修飾子を持つ要素を返すことが明示的に指定されるべきであるとされています。つまり、ユーザーが指定した修飾子と同じ修飾子を持つ要素を返すことで、入力バッファのconst修飾を無意識に失うことを防ぐことができます。
提案の根拠として、既存のAPIはC11がジェネリックでオーバーロードされた関数の定義を導入する前に設計され、ライブラリに追加されました。したがって、入力パラメータには暗黙的にconst修飾を追加することができるため、const修飾されたバッファと変更可能なバッファの両方を検索することができるように、既存のシグネチャが設計されました。正しく使用されれば、返されるポインタの修飾が不足している場合でも、適切に修飾された結果ポインタに割り当てることで、同じ修飾が復元されます。これにより、検索関数はジェネリックな機能を提供する原始的な形式を提供します。
しかし、ユーザーが返されたポインタの変数が正しいタイプであることを手動で確認する必要があります。ユーザーがconst修飾されたバッファを検索して、結果を変更可能なオブジェクトのポインタに割り当てた場合、APIには直接的にこれがタイプエラーであることを伝える方法がありません。実装によっては、警告を表示することができるかもしれませんが、これは言語で伝えられるものを超えて、ライブラリ関数とその契約に関するハードコーディングされた知識を必要とするでしょう。
プリプロセッサディレクティブ #embed が追加された。 バイナリーデータを可能な限り忠実に埋め込む機能
nullptr定数とnullptr_t型が追加されました。 voidへのポインタと同じように定義された基本表現を持ちます。N3042
__VA_OPT__ は、マクロのパラメーターとして受け取った可変引数が空でない場合に置換するトークンを指定します。C++20
可変モディファイ型( Variably-Modified Types )のサポートを義務付けられました(ただし、可変長配列そのものではありません)。N2992
関数定義の引数の省略記号( ... )は、関数のパラメーターリストにおいて先行するパラメータなしで現れることができるようになりました。 va_startはもはやそのような引数が渡されることを必要としません。
Unicode 識別子が、UAX(Unicode Standard Annex) #31 に従うようになりました。
[TODO:#ハイライトの完成]
ANSI Cのプロトタイプ宣言以前の、K&R1形式の関数宣言が廃止されました。
# を ??= の3文字で表すトリグラフ(JIS語では「3文字表記」)が規格から削除されました。C++では既にC++17で削除されています。
「C言語から2の補数以外の符号表現を削除することで、整数型の仕様を大幅に簡素化することができる」とし「他の符号表現の仕様」を排除しました。
二進数リテラルが追加されました。二進数リテラルは、接頭辞0bまたは0Bの後に0または1の数字が連続したものです。C++では既にC++14で二進数リテラルが導入されています。
二進数リテラルに呼応する形でprintf系とscanf系の関数に書式 %b が追加されました。
属性( attribute )は、一般化された構文を持つ言語エンティティに、開発者が各機能のために新しい構文やキーワードを導入する代わりに、追加の情報を付加できるメカニズムです。
[TODO:具体例]
型推論( Type inference; TI )とは、宣言がその初期化子として使用される式から型を推論することを可能にする機能です。 N3007提案では、auto i = 123L; の様に「型」の部分に auto と書くと初期化子(この場合は 123L)から変数(この場合は i)の型(この場合は long)を言語処理系が推論するという構文です(概ねC++と同じですが、C++には参照やラムダ式があるので若干事情が違います)。 auto は今までもキーワードでしたが、(静的変数に対して)「自動変数」を宣言することを表すもので、省略可能でした。
この例の
は、これまでならば
と書くところですが、型推論を使い型名を明示しないで済んでいます。