C99では、C++(C++98)にはあるいくつかの型がサポートされません。逆に、C99にはあってC++にはない型もあります。また、型変換についてはかなり仕様が異なりますので、今回はそのあたりの解説を行います。
C99にはない型
構造体と共用体以外のクラスがないのは当然として、それ以外にも、C++にはあるけれどもC言語にはない型がいくつかあります。
bool型
C99にはbool型が存在しません。ただし、論理型として_Bool型があります。また、<stdbool.h> では、bool, true, falseという名のマクロが、それぞれ、_Bool, 1, 0 に定義されます。しかし、旧規格との互換性のため、論理値をあらわす必要があるときには、普通はint型を使います。したがって、等価演算子(== および !=)、関係演算子(<, <=, >, および >=)、論理演算子(!, &&, および ||)の評価結果は、C++ではbool型でしたが、C99ではint型になります。
wchar_t型
C99には、基本型としてのwchar_t型がありません。しかし、ワイド文字もワイド文字列も扱うことができます。では、どうしているかというと、wchar_t型は基本型ではありませんが、typedef名として標準ライブラリで定義されています。wchar_t 型を定義しているヘッダは、<stddef.h>, <stdlib.h>, <wchar.h>, <wctype.h> です。
参照型
C++には、オブジェクトに別名を付けるための純粋な参照型がありましたが、C99には参照型はありません。参照が必要な場合は、ポインタを使うことになります。
メンバへのポインタ型
C99には、構造体や共用体のメンバへのポインタ型というものがありません。それにともない、.*演算子や->*演算子も存在しません。メンバへのポインタと同等のことを行うには、構造体や共用体へのポインタと指定するメンバのオフセット値を併用する必要があります。メンバのオフセット値はoffsetofマクロで取得することができます。
C++になく、C99にはある型
C言語の旧規格やC++にはないけれども、C99にはある型がいくつか存在します。
long long型
C99には、少なくとも64ビット以上あるlong long型、およびunsigned long long型が存在します。C++98でもlong long型をサポートする処理系がありますが、あくまでも処理系の独自拡張にすぎません。
long long型が標準でサポートされていることで、整数定数の型がC++(およびC言語の旧規格)とは微妙に異なっています。具体的には、C++では、long型で表現できない10進整数定数はunsigned long型になりましたが、C99では、long long型になります。そして、long long型で表現できない10進整数定数はunsigned long long型になります。8進または16進整数定数の場合、long long型で表現できなければunsigned long型に、unsigned long型でも表現できなければlong long型に、それでも表現できなければunsigned long long型になります。
整数定数がlong long型であることを指定するには、添字としてLLを、unsigned long long型であることを指定するには、添字としてULLを付加します。これらの添字は、大文字でも小文字でもかまいません。
複素数型
C++では、複素数はstd::complexクラステンプレートを用いて表現しますが、C99には複素数を表現するための組み込み型があります。複素数型を表すには_Complexというキーワードを用います。そして、float _Complex, double _Complex, long double _Complexのように精度を指定します。
<complex.h>では、_Complexに展開されるマクロcomplexが定義されています。また、虚数単位を表すマクロIも定義されています。そして、複素数型の定数は、1 + 2 * Iのように表現することができます。
処理系によっては、純虚数を表現するために_Imaginaryというキーワードを使えることがあります。そして、float _Imaginary, double _Imaginary, long double _Imaginaryのように精度を指定します。_Imaginaryを使える場合、<complex.h>では_Imaginaryに展開されるマクロ imaginary が定義されます。_Imaginaryは任意実装ですので、常にどんな処理系でも使えるわけではありません。
可変長配列
C99では、実行時に要素数を決定することができる可変長配列がサポートされています。ただし、静的記憶域期間を持つ可変長配列を宣言することはできません。可変長配列は、次のようにして使います。
0 1 2 3 |
int n = 10; int array[n]; |
可変長配列が存在することで、C99におけるsizeofの式は、必ずしも定数式になるとは限りません。
暗黙的な型変換
C++にくらべて、C99はかなり豪快に暗黙的な型変換が行われます。具体的には、オブジェクト型または不完全型へのポインタは、任意のオブジェクト型または不完全型へのポインタに暗黙的に型変換することができます。例えば、char* から int* とか、void*からdouble*とかに自由に型変換できるわけです。もし間違った代入などが行われていても、コンパイラは警告を出すことはあってもエラーにすることはありません。
0 1 2 3 4 5 6 7 8 |
int main(void) { char *s = "abc"; int *p1 = s; /* OK */ double *p2 = malloc(sizeof(double)*10); /* OK */ return 0; } |
さらには、ポインタ型と汎整数型のあいだでも暗黙的な型変換が可能です。
0 1 2 3 4 5 6 7 8 |
int main(void) { char *s = "abc"; int a = s; /* OK */ int *p = 1234; /* OK */ return 0; } |
さらに、関数へのポインタは、任意の関数へのポインタ型に暗黙的な型変換を行うことができます。仮引数の型や個数が異なっていても、返却型がまったく異なっていても、何のチェックもされません。
このように、C++ではコンパイルエラーになるような暗黙の型変換ができてしまいます。多くの場合、こうした暗黙の型変換は間違いですが、コンパイラはエラーにしてくれません。プログラマは自分の責任において、こうした間違いを防がなければなりません。
明示的な型変換
C99には、const_cast, dynamic_cast, reinterpret_cast, およびstatic_cast演算子がありません。使えるキャスト演算子は、(型名) だけです。int(a)のような関数形式のキャストも使うことができません。