[C99] 第9回 リテラル

リテラルについても、C++とC99では微妙なちがいがあります。

文字定数

C++では「文字リテラル」といいますが、C99では「文字定数」という用語が使われています。

C++の文字リテラルは char 型でしたが、C99の文字定数は int 型です。C99では多重定義(オーバーロード)がありませんので、sizeof 演算子のオペランドに文字定数を指定しないかぎり、この違いが問題になることはとくにないでしょう。

C99でも、ワイド文字定数は wchar_t 型です。しかし、wchar_t 型自体が基本型ではなく、実体は一般の汎整数型ですので、その意味ではC++とは異なります。しかし、これも具体的に問題になることはまずないでしょう。

文字列リテラル

C++の文字列リテラルは const char 型の配列でしたが、C99の文字列リテラルは char 型の配列です。しかし、文字列リテラルの各要素に書き込んだ場合の動作は未定義です。

文字列リテラルによる配列の初期化

文字列リテラルを使った配列の初期化では、C++とC99では微妙な違いがあります。C++では、3要素しかない配列を3文字の文字列リテラルで初期化しようとするとエラーになりました。しかし、C99では、終端のナル文字が格納されることなく、コンパイルできてしまいます。

char s[3] = "abc";  /* OK */

上記のコードは、{ 'a', 'b', 'c' } を初期化子にした場合と同じ結果になります。

文字列リテラルとワイド文字列の連結*1

C99では、文字列リテラルとワイド文字列リテラルを連結することができます。異なる種類の文字列リテラルを連結した場合はワイド文字列リテラルになります。

s = L"abc" "123";

上記のコードは次のコードと等価になります。

s = L"abc123";

整数定数

C++では「整数リテラル」といいますが、C99では「整数定数」という用語が使われています。また、C99にはlong long型があるため、整数定数がどんな型になるかがC++では微妙に異なります。

C++では、long型で表現できない10進整数リテラルはunsigned long型になりました。しかし、C99では、long型で表現できない10進整数リテラルはlong long型になり、long long型でも表現できなければunsigned long long型になります。

真理値定数

C++には、true および false という真理値リテラルがありました。C99には真理値を表す_Bool型がありますが、真理値を表す特別な定数はなく、整数定数である 1 と 0 を使います。<stdbool.h>ヘッダでは true および false マクロが定義されますが、これらは単なる整数定数である 1 および 0 に展開されます。

複合リテラル*2

C++のクラスはコンストラクタを定義することができますが、C言語の構造体や共用体ではコンストラクタを定義することができません。そのため、通常であれば、いったん初期化子を指定したオブジェクトを定義し、それを使うしかありません。ところが、そうした方法では、文を使える文脈であればよいのですが、式しか使えないような文脈では簡潔な書き方ができなくなります。

C99では、構造体・共用体・配列に関して、「複合リテラル」という一種のリテラルを使うことがでいます。具体的には次のように書きます。

struct A { int i; const char *s; };
struct A a;
a = (struct A){ 123, "abc" };

C++では、配列にコンストラクタは定義できませんし、vectorなどのコンテナでも要素を一つ一つ指定したコンストラクタの呼び出しはできません。それに対して、C99では、配列に関しても同様に複合リテラルが使えますので、かなり強力になっています。

int a[10];
memcpy(a, (int[]){ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, sizeof(int) * 10);

もちろん、構造体型の配列や多次元配列など、複雑な型であっても複合リテラルを使うことができます。また、複合リテラルに要素指示子を使うこともできます。


*1 C90では、C++と同様、文字列リテラルとワイド文字列リテラルを連結しようとした場合の動作は未定義になります。
*2 C90には複合リテラルはありません。

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

http://www.kijineko.co.jp/trackback/861