第5回 型と型変換

Cでは、C++にはあるいくつかの型がサポートされません。また、型変換についてはかなり仕様が異なりますので、今回はそのあたりの解説を行います。

Cにはない型

構造体と共用体以外のクラスがないのは当然として、それ以外にも、C++にはあるけれどもCにはない型がいくつかあります。

bool 型

Cには bool 型が存在しません。そのため、論理値をあらわす必要があるとき、普通は int 型を使います。したがって、等価演算子(== および !=)、関係演算子(<, <=, >, および >=)、論理演算子(!, &&, および ||)の評価結果は、C++では bool 型でしたが、Cでは int 型になります。

wchar_t 型

Cには、基本型としての wchar_t 型がありません。しかし、ワイド文字もワイド文字列も扱うことができます。では、どうしているかというと、wchar_t 型は基本型ではありませんが、typedef 名として標準ライブラリで定義されています。wchar_t 型を定義しているヘッダは、<stddef.h> および <stdlib.h> です。

参照型

C++には、オブジェクトに別名を付けるための純粋な参照型がありましたが、Cには参照型はありません。参照が必要な場合は、ポインタを使うことになります。

メンバへのポインタ型

Cには、構造体や共用体のメンバへのポインタ型というものがありません。それにともない、.* 演算子や ->* 演算子も存在しません。メンバへのポインタと同等のことを行うには、構造体や共用体へのポインタと指定するメンバのオフセット値を併用する必要があります。メンバのオフセット値は offsetof マクロで取得することができます。

暗黙的な型変換

C++にくらべて、Cはかなり豪快に暗黙的な型変換が行われます。具体的には、オブジェクト型または不完全型へのポインタは、任意のオブジェクト型または不完全型へのポインタに暗黙的に型変換することができます。例えば、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++ではコンパイルエラーになるような暗黙の型変換ができてしまいます。多くの場合、こうした暗黙の型変換は間違いですが、コンパイラはエラーにしてくれません。プログラマは自分の責任において、こうした間違いを防がなければなりません。

明示的な型変換

Cには、const_cast, dynamic_cast, reinterpret_cast, および static_cast 演算子がありません。使えるキャスト演算子は、(型名) だけです。int(a) のような関数形式のキャストも使うことができません。