定数の指定方法

ユニバーサルキャラクタ

 従来の仕様では,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'
  $

bool型について

 言語仕様の中では規定されていなかったのですが,たいていのC言語環境ではboolが定義されています.C99規格では,以下のように規定されました.

・bool型は符号なしの型として扱われる
・bool型は0と1を格納できれば十分なサイズ
・bool型に変換する場合の値は0か1である
・bool型は標準整数型よりもサイズが小さい
・bool型をビットフィールドに定義することができる

 そして重要なことは,今まで定義されていたboolとコンフリクトしないように,_Bool型という名称に変更されたことです.ただし,本連載で基準としているGCC2.95では対応できていません.

 リスト8リスト9のコンパイルおよび実行結果を以下に示します.

〔リスト8〕C99規格の_Booltest113.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 long int型

 従来の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 inttest115.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でも実装されていないようです.

Copyright 2003 岸 哲夫

Copyright 1997-2024 CQ Publishing Co.,Ltd.

 

NEW記事内インデックス    連載インデックスはこちら   Interfaceのトップ
C99規格の予約語について/新しく定義されたマクロとプラグマ/C99規格のマクロ/その他のプリプロセッサ関連
◆定数の指定方法/配列について/bool型について/long long int型/整数除算に関する規定/複素数型
暗黙の関数宣言と変数宣言/定義済みマクロ__func__と可変長引き数マクロ/列挙型について/inline関数定義/restrictポインタ/変数宣言の位置/配列の初期化について/コンパウンドリテラル(Compound Literal)
isblank/関数やマクロの概略