[C99] 第5回 型と型変換

C99では、C++にはあるいくつかの型がサポートされません。逆に、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++でも 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では、実行時に要素数を決定することができる可変長配列がサポートされています。ただし、静的記憶域期間を持つ可変長配列を宣言することはできません。可変長配列は、次のようにして使います。

int n = 10;
int array[n];

可変長配列が存在することで、C99における sizeof の式は、必ずしも定数式になるとは限りません。

暗黙的な型変換

C++にくらべて、C99はかなり豪快に暗黙的な型変換が行われます。具体的には、オブジェクト型または不完全型へのポインタは、任意のオブジェクト型または不完全型へのポインタに暗黙的に型変換することができます。例えば、char* から int* とか、void* から double* とかに自由に型変換できるわけです。もし間違った代入などが行われていても、コンパイラは警告を出すことはあってもエラーにすることはありません。

int main(void)
{
  char *s = "abc";
  int *p1 = s;  /* OK */
  double *p2 = malloc(sizeof(double)*10);  /* OK */
  return 0;
}

さらには、ポインタ型と汎整数型のあいだでも暗黙的な型変換が可能です。

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) のような関数形式のキャストも使うことができません。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/826
このエントリーを含むはてなブックマーク