型
(ほとんどの組み込み型の詳細については算術型も参照してください。 C のライブラリによって提供されている型関連ユーティリティの一覧も参照してください。)
オブジェクト、関数、および式には、型と呼ばれる性質があり、オブジェクトに格納されたまたは式によって評価されたバイナリ値の解釈を決定します。
目次 |
[編集]型の分類
C の型システムは以下の型から構成されます。
- void 型
- 基本型
- char 型
- 符号付き整数型
- 標準: signedchar, short, int, long, longlong(C99以上)
- 拡張: 処理系定義 (__int128 など)
- 符号なし整数型
- 標準: _Bool(C99以上), unsignedchar, unsignedshort, unsignedint, unsignedlong, unsignedlonglong(C99以上)
- 拡張: 処理系定義 (__uint128 など)
- 浮動小数点型
- 実数浮動小数点型: float, double, longdouble
- 複素数型: float _Complex, double _Complex, longdouble _Complex
- 虚数型: float _Imaginary, double _Imaginary, longdouble _Imaginary
- 派生型
上記のすべての型について、 const、 volatile、および restrict 修飾子のうちの1つ、2つ、または3つすべての組み合わせに対応する、その型の修飾されたバージョンがあります (その修飾子の意味論が許す場合のみ)。
[編集]型のグループ
- オブジェクト型: 関数型でないすべての型
- 文字型: char, signedchar, unsignedchar
- 整数型: char、符号付き整数型、符号なし整数型、列挙型
- 実数型: 整数型および実数浮動小数点型
- 算術型: 整数型および浮動小数点型
- スカラー型: 算術型およびポインタ型
- 集成体型: 配列型および構造体型
- 派生宣言子型: 配列型、関数型、およびポインタ型
[編集]互換な型
C のプログラムでは、異なる翻訳単位の同じオブジェクトまたは関数を参照する宣言が同じ型を持つ必要はありません。 充分に似ている型 (形式的には互換な型と言います) を使用する必要があるだけです。 同じことが関数呼び出しおよび左辺値アクセスにも適用されます。 実引数は仮引数の型と互換でなければならず、左辺値式の型はアクセスされるオブジェクトの型と互換でなければなりません。
以下の場合、型 T
と型 U
は互換です。
- 同じ型である (同じ名前であるか、 typedef によって導入されたエイリアスである)。
- 互換な修飾されていない型の、同等に cvr 修飾されたバージョンである。
- それらがポインタ型であり、互換な型を指す。
- それらが配列型であり、
- その要素型が互換であり、
- サイズが定数であるならば、そのサイズが同じである。 ノート: 境界が未知な配列は要素型が互換なあらゆる配列と互換です。 VLA は要素型が互換なあらゆる配列と互換です。(C99以上)
- それらが構造体/共用体/列挙型であり、
- (C99) 一方がタグを用いて宣言されていて、他方もその同じタグを用いて宣言されている。
- それらが完全型の場合は、それらのメンバは、個数が同じで、互換な型を用いて宣言され、一致する名前を持たなければなりません。
- さらに、それらが列挙型の場合は、対応するメンバは同じ値を持たなければなりません。
- さらに、それらが構造体または共用体の場合は、
- 対応するメンバは同じ順序で宣言されなければなりません (構造体のみ)。
- 対応するビットフィールドは同じ幅でなければなりません。
- 一方が列挙型であり、他方がその列挙のベースとなる型である。
- それらが関数型であり、
- その戻り値の型が互換である。
- どちらも仮引数リストを持ち、仮引数の個数 (省略記号を含みます) が同じであり、対応する仮引数 (配列からポインタ、関数からポインタへの型調節およびトップレベルの修飾子の除去後の) が互換な型である。
- 一方が旧形式 (仮引数なし) の定義であり、他方が仮引数リストを持ち、その仮引数リストが省略記号を使用せず、その仮引数のそれぞれ (関数引数の型調節後) が対応する旧形式の仮引数のデフォルト引数昇格後と互換である。
- 一方が旧形式 (仮引数なし) の宣言であり、他方が仮引数リストを持ち、その仮引数リストが省略記号を使用せず、すべての仮引数 (関数引数の型調節後) がデフォルト引数昇格の影響を受けない。
char 型は signedchar とも unsignedchar とも互換ではありません。
2つの宣言が同じオブジェクトまたは関数を参照し、それらが互換な型を使用していない場合、プログラムの動作は未定義です。
// 翻訳単位 #1struct S {int a;};externstruct S *x;// #2 の x とは互換ですが、 #3 の x とは非互換です。// 翻訳単位 #2struct S;externstruct S *x;// どちらの x とも互換です。// 翻訳単位 #3struct S {float a;};externstruct S *x;// #2 の x とは互換ですが、 #1 の x とは非互換です。 // 動作は未定義です。
// 翻訳単位 #1#include <stdio.h>struct s {int i;};// #3 の s とは互換ですが、 #2 の s とは非互換です。externstruct s x ={0};// #3 の x と互換です。externvoid f(void);// #2 の f と互換です。int main(){ f();return x.i;}// 翻訳単位 #2struct s {float f;};// #4 の s とは互換ですが、 #1 の s とは非互換です。externstruct s y ={3.14};// #4 の y と互換です。void f()// #1 の f と互換です。{return;}// 翻訳単位 #3struct s {int i;};// #1 の s とは互換ですが、 #2 の s とは非互換です。externstruct s x;// #1 の x と互換です。// 翻訳単位 #4struct s {float f;};// #2 の s とは互換ですが、 #1 の s とは非互換です。externstruct s y;// #2 の y と互換です。 // 動作は well-defined です。// オブジェクトと関数の宣言のみが互換でなければなりません (型自体は含まれません)。
ノート: C++ には互換な型という概念はありません。 異なる翻訳単位で互換だけれども同一ではない2つの型を宣言する C のプログラムは、有効な C++ のプログラムではありません。
[編集]合成型
合成型は互換な2つの型から構築できます。 これはそれら2つの型どちらとも互換な、以下の条件を満たす型です。
- 両方の型が配列型であれば、以下のルールが適用されます。
- 一方の型がサイズが既知かつ定数の配列であれば、合成型はそのサイズの配列です。
| (C99以上) |
- そうでなければ、両方の型がサイズの未知な配列であり、合成型はサイズの未知な配列です。
- 合成型の要素型は2つの要素型の合成型です。
- 一方の型が仮引数型リストを持つ関数型 (関数プロトタイプ) で他方がそうでない場合、合成型はその仮引数型リストを持つ関数プロトタイプです。
- 両方の型が仮引数方リストを持つ関数型の場合、合成型の仮引数型リスト内のそれぞれの仮引数の型は対応する仮引数の合成型です。
これらのルールは2つの型の派生元の型に再帰的に適用されます。
// 以下の2つのファイルスコープの宣言が与えられた場合、int f(int(*)(), double(*)[3]);int f(int(*)(char*), double(*)[]);// この関数の合成型は以下のようになります。int f(int(*)(char*), double(*)[3]);
その識別子の以前の宣言が可視なスコープ内で宣言された内部または外部リンケージを持つ識別子の場合、その以前の宣言が内部または外部リンケージを指定しているならば、後の宣言における識別子の型は合成型になります。
[編集]不完全型
不完全型はその型のオブジェクトのサイズを決定するための情報が充分でないオブジェクト型です。 不完全型は翻訳単位内のどこかの時点で完全化されることもあります。
以下の型は不完全です。
- void 型。 この型は完全化できません。
- サイズの未知な配列型。 サイズを指定する後の宣言で完全化できます。
externchar a[];// a の型は不完全です (これは一般的にはヘッダ内に現れます)。char a[10];// a の型は完全になります (これは一般的にはソースファイル内に現れます)。
- 内容が未知の構造体または共用体型。 内容を定義する同じ構造体または共用体の同じスコープ内の後の宣言で完全化できます。
struct node {struct node *next;// 構造体 node はこの時点では不完全です。};// 構造体 node はこの時点で完全になります。
[編集]型名
型は宣言以外の文脈で指定する必要があることもあります。 そのような状況では型名が使用されます。 これは、文法的には、その型の単一のオブジェクトまたは関数を宣言するために使用される、型指定子および型修飾子のリストに宣言子が続いたもの (宣言を参照) と正確に同一ですが、識別子は省かれます。
int n;// int の宣言。sizeof(int);// 型名の使用。 int*a[3];// int へのポインタ3個の配列の宣言。sizeof(int*[3]);// 型名の使用。 int(*p)[3];// int 3個の配列へのポインタの宣言。sizeof(int(*)[3]);// 型名の使用。 int(*a)[*]// (関数の仮引数における) VLA へのポインタの宣言。sizeof(int(*)[*])// (関数の仮引数における) 型名の使用。 int*f(void);// 関数の宣言。sizeof(int*(void));// 型名の使用。 int(*p)(void);// 関数へのポインタの宣言。sizeof(int(*)(void));// 型名の使用。 int(*const a[])(unsignedint, ...)={0};// 関数へのポインタの配列の宣言。sizeof(int(*const[])(unsignedint, ...));// 型名の使用。
ただし、識別子の周りの冗長な括弧は型名内では意味を持ち、「引数仕様を持たない関数」を表します。
int(n);// int 型の n を宣言します。sizeof(int());// 「int を返す関数」型の使用。
型名は以下の状況で使用されます。
(C99以上) | |
(C11以上) |
型名は新しい型を導入することもあります。
void* p =(void*)(struct X {int i;}*)0;// キャスト式内で使用されている型名「struct X {int i;}*」は// 新しい型「struct X」を導入します。struct X x ={1};// struct X はスコープ内です。
[編集]参考文献
- C11 standard (ISO/IEC 9899:2011):
- 6.2.5 Types (p: 39-43)
- 6.2.6 Representations of types (p: 44-46)
- 6.2.7 Compatible type and composite type (p: 47-48)