● ユニバーサルキャラクタ
従来の仕様では,1バイト文字列または文字定数の定義については明確に定められていましたが,2バイト文字に関しては定められていませんでした.
C99規格では,この記法をユニバーサルキャラクタと呼んでいます.たとえば,
const wchar_t str[] = L"\u4E16\u754C";
のような記法ができるはずなのですが,GCCではまだ対応できていません.unicodeを使用して,日本語,英語以外の言語でプログラミングする場合に便利な機能だと思いますが,通常は必要ないと思います.
● プログラム中で使用できる文字種
従来の仕様では識別名に,マルチバイト文字やユニバーサルキャラクタを使用することは不可能でした.しかし,C99規格ではそれが可能になりましたが,GCCではまだ対応ができていません.
unicodeならともかく,環境に依存したマルチバイト文字を使用した場合,可搬性に大きな問題が発生してしまいます.したがって,実装されても使い道はないように思えます.
● 浮動小数点定数を16進数で表記する
浮動小数点の誤差を減らすために,定数の表記を以下のように変更できます.
float f = 0x000f.ddddP0;
0x000fの部分は整数で16進値,ddddの部分が小数で16進値,最後のゼロは指数です.
GCCでは,拡張機能として導入されていました.-ansiを指定した場合にはエラーになります.
リスト5の実行結果を以下に示します.
〔リスト5〕浮動小数点定数の表記(test110.c)
|
/*
*浮動小数点の表記
*/
main()
{
float f = 0x000f.ddddP0;
printf("%f\n",f);
}
|
|
$ gcc test110.c -o test110
$ ./test110
15.866653
$ gcc -ansi -pedantic test110.c -o test110
test110.c: In function `main':
test110.c:6: floating constant may not be in radix 16
$
● 可変長自動配列
C99規格では,可変長自動配列を使用することができます.連載第7回の拡張機能として説明したものと同一です.
● 長さが0の配列
C99規格では,長さが0の配列を使用することができます.これもまた,GCCの拡張機能としてすでに実装されています.-ansiを指定した場合にはエラーになります.
リスト6のコンパイル結果は,以下のようになります.
〔リスト6〕長さが0の配列(test111.c)
|
/*
*長さが0の配列
*/
typedef struct zerotbl {
int youso1;
int youso2[0];
}zerotblstr;
main()
{
zerotblstr *str = (zerotblstr *)malloc(sizeof(zerotblstr) + sizeof(int) * 10);
}
|
|
$ gcc test111.c -o test111
$ gcc -ansi -pedantic test111.c -o test111
test111.c:11: warning: file does not end in newline
test111.c:6: warning: ANSI C forbids zero -size array `youso2'
$
● 配列要素に記憶修飾子,型修飾子を記述する
C99規格では,配列要素に記憶修飾子や型修飾子を記述することが可能になりましたが,GNU Cはこれに対応していません.コンパイルエラーになります.
リスト7のコンパイル結果を以下に示します.
〔リスト7〕記憶修飾子,型修飾子を記述(test112.c)
|
/*
*配列要素に記憶修飾子、型修飾子を記述する
*/
typedef struct zerotbl {
int youso1;
int youso2[0];
}zerotblstr;
main()
{
zerotblstr *str = (zerotblstr *)malloc(sizeof(zerotblstr) + sizeof(int) * 10);
test1(int test1_1[static 100]);
test2(int test2_1[restrict],int test2_2[restrict ]);
test3(int test3_1[const]);
test4(int test4_1[volatile]);
}
|
|
$ gcc test112.c -o test112
test112.c: In function `main':
test112.c:11: parse error before `int'
test112.c:12: parse error before `int'
test112.c:13: parse error before `int'
test112.c:14: parse error before `int'
$
言語仕様の中では規定されていなかったのですが,たいていのC言語環境ではboolが定義されています.C99規格では,以下のように規定されました.
・bool型は符号なしの型として扱われる
・bool型は0と1を格納できれば十分なサイズ
・bool型に変換する場合の値は0か1である
・bool型は標準整数型よりもサイズが小さい
・bool型をビットフィールドに定義することができる
そして重要なことは,今まで定義されていたboolとコンフリクトしないように,_Bool型という名称に変更されたことです.ただし,本連載で基準としているGCC2.95では対応できていません.
リスト8とリスト9のコンパイルおよび実行結果を以下に示します.
〔リスト8〕C99規格の_Bool型(test113.c)
|
/*
*_Bool型
*/
#include <stdbool.h>
main()
{
_Bool a;
printf("%d\n",sizeof(_Bool));
printf("%d\n",sizeof(short));
printf("%d\n",sizeof(int));
}
|
|
〔リスト9〕従来のbool型およびそのサイズ(test114.c)
|
/*
*_Bool型
*/
#include <stdbool.h>
main()
{
printf("bool型の大きさ=%d\n",sizeof(bool));
printf("short型の大きさ=%d\n",sizeof(short));
printf("int型の大きさ=%d\n",sizeof(int));
}
|
|
$ gcc -S test113.c
test113.c: In function `main':
test113.c:7: `_Bool' undeclared (first use in this function)
test113.c:7: (Each undeclared identifier is reported only once
test113.c:7: for each function it appears in.)
test113.c:7: parse error before `a'
$ gcc -S test114.c
$
$ gcc test114.c -o test114
$ ./test114
bool型の大きさ=4
short型の大きさ=2
int型の大きさ=4
$
このように,_Boolではコンパイルが通りません.そして,bool型のサイズも従来と変わりません.
従来のlong型を使って,たとえばお金に関する計算をしようとすると,そのままでは10億の単位が最大でした.これでは地方自治体の予算さえ扱えません.しかし,C99規格で規定されたlong long int型を使えば,1000京まで扱えます.これで,トルコリラでもウォンでもペソでも計算できるでしょう.
そのサイズに関しては,ヘッダファイルlimits.hで定義されています.
符号付きの場合は,絶対値9,223,372,036,854,775,807まで保持できます.また,符合なしの場合,その2倍に1を加えた18,446,744,073,709,551,615まで保持できます.
なお,64ビット環境ではlong型とlong long int型は同等です.
もっとも,GNU CやUNIX環境の一部では,以前からlong long int型が定義されていました.これも,それぞれの環境で取り入れていた拡張機能が標準になった例です.
リスト10の実行例を以下に示します.
〔リスト10〕long long int型(test115.c)
|
/*
*long long int型
*/
#include <stdio.h>
main()
{
long long int big_int = 9223372036854775807LL;
printf("longlong int型の大きさ=%d\n",sizeof(long long int));
printf("big_intの値=%lld\n",big_int);
}
|
|
$ gcc test115.c -o test115
$ ./test115
longlong int型の大きさ=8
big_intの値=9223372036854775807
$
ソースを見るとわかるとおり,定数として扱う場合のサフィックスはLL,llです.符号付きはUが付いてULL,ullです.
また,以下のように-ansiオプション付きでコンパイルすると,警告が出ます.
$ gcc -ansi -pedantic test115.c -o test115
test115.c:10: warning: file does not end in newline
test115.c: In function `main':
test115.c:7: warning: ANSI C does not support `long long'
test115.c:7: warning: ANSI C forbids long long integer constants
test115.c:8: warning: ANSI C does not support `long long'
$
これについては,連載第3回で触れています.
従来のC言語の規格では,両方のオペランドが正の数の場合,商は小数点以下を捨てた数になることになっています.たとえば,1/2の場合,正しい値は0.5ですが,小数点以下を捨てるので商は0,余りが1になります.
どちらか一方の数が負の場合の計算結果は,環境に依存していましたが,C99規格で規定されることになりました.それは,割り算の結果はすべて0方向に切り捨てるということです.
リスト11のソースの実行結果を以下に示します.
〔リスト11〕整数除算に関する規定(test116.c)
|
/*
*整数の除算
*/
#include <stdio.h>
main()
{
printf("-1/2 =%d\n",-1/2 );
printf("-1.0f/2.0f =%f\n",-1.0f/2.0f );
printf("-1/2.0f =%f\n",-1/2.0f );
printf("-1.0f/2 =%f\n",-1.0f/2 );
}
|
|
$ gcc test116.c -o test116
$ ./test116
-1/2 =0
-1.0f/2.0f =-0.500000
-1/2.0f =-0.500000
-1.0f/2 =-0.500000
$
上記のように,オペランドの両方が整数の場合は0方向に切り捨てられます.
どちらか,または一方が浮動小数点型の場合には,結果は切り捨てられません.
_Complex型は,C99規格で決定される以前から通常の環境で拡張機能として利用されていました.
通常の環境では,構造体で実数部と虚数部を定義して実装しています.
GNU Cでは実数部をレジスタに置き,虚数部をスタック上に置くことも,またその逆も可能ですが,その状態を管理できるデバッガがありません.
複素数の自動変数を非連続的な形で割り当てることができますが,その場合,あたかもそれが二つの別々の非複素数変数であるかのように記述するので,デバッグの際に注意してください.
虚数定数にはサフィックスiが付きます.よって,float _Complexは,10.25f + 25.65iなどと表記することが可能です.
リスト12の実行結果を以下に,生成されたアセンブラソースをリスト13に示します.
〔リスト12〕簡単な複素数の計算(test117.c)
|
/*
*_Complex
*/
#include <stdio.h>
#include <complex.h>
main()
{
float _Complex a;
int _Complex ai;
double _Complex b;
long double _Complex c;
float aa;
float bb;
int aai;
int bbi;
a = 15.253f + (float _Complex)50.235i;
aa = __imag__ a;
bb = __real__ a;
printf("%f\n",aa);
printf("%f\n",bb);
ai = 1 + (int _Complex)2i;
aai = __imag__ ai;
bbi = __real__ ai;
printf("%d\n",aai);
printf("%d\n",bbi);
}
|
|
〔リスト13〕生成されたアセンブラソース(test117.s)
|
.file "test117.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.align 4
.LC0:
.long 0x41740c4a
.long 0x4248f0a4
.LC1:
.string "%f\n"
.align 4
.LC2:
.long 1
.long 2
.LC3:
.string "%d\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $104,%esp
flds .LC0
fstps -8(%ebp)
flds .LC0+4
fstps -4(%ebp)
flds -4(%ebp)
fstps -68(%ebp)
flds -8(%ebp)
fstps -72(%ebp)
addl $-4,%esp
flds -68(%ebp)
subl $8,%esp
fstpl (%esp)
pushl $.LC1
call printf
addl $16,%esp
addl $-4,%esp
flds -72(%ebp)
subl $8,%esp
fstpl (%esp)
pushl $.LC1
call printf
addl $16,%esp
movl .LC2,%eax
movl %eax,-16(%ebp)
movl .LC2+4,%eax
movl %eax,-12(%ebp)
movl -12(%ebp),%eax
movl %eax,-76(%ebp)
movl -16(%ebp),%eax
movl %eax,-80(%ebp)
addl $-8,%esp
movl -76(%ebp),%eax
pushl %eax
pushl $.LC3
call printf
addl $16,%esp
addl $-8,%esp
movl -80(%ebp),%eax
pushl %eax
pushl $.LC3
call printf
addl $16,%esp
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
$ gcc -S test117.c
$ gcc test117.c -o test117
$ ./test117
50.235001
15.253000
2
1
$
複素数における虚数部の値を示すための型である_Imaginary型については,GNU C2.95ではまだサポートされていません.
リスト12のソースのように,
aai = __imag__ ai;
bbi = __real__ ai;
のような使い方ができるので,不便ではないと思います.
次回でバージョンごとのC99規格の実装を明確にする予定ですが,GCC3.3でも実装されていないようです.
|