次に,C言語のいろいろな表現方法のサポートを行うオプションについて説明します.
● -ansi
ANSI規格にのっとったCプログラムをサポートします.
このオプションは,ANSI C規格と互換性のないGCCの機能をオフにします.オフになる機能としては,キーワードasm,typeof,システムの形式を特定するunixやvaxなどの定義済みマクロなどがあります.また,ANSIのトリグラフの機能を有効にします.Cコンパイラの場合は,C++形式の//によるコメントやキーワードinlineを無効にします.C++コンパイラの場合には,-foperator-namesが有効になります.
-ansiを指定しても,別の形のキーワードである__asm__,__extension__,__inline__,__typeof__は使用可能です.
ただ,-ansiオプションを指定しただけでは,非ANSIのプログラムを拒絶することはできません.拒絶するには,-ansiに加えて-pedanticを指定する必要があります.
-ansiオプションを指定すると,マクロ__STRICT_ANSI__が定義済みになります.ヘッダファイルの中には,このマクロが定義されていると,ANSI規格では使用しない関数の宣言やマクロの定義をしないようにするものもあります.これは,そのような関数やマクロの名前を別のものに使っているかもしれないプログラムとの干渉を避けるためですが,意図しない場合には注意しなくてはなりません.
なお,関数alloca,abort,exit,_exitは,-ansiが指定されると組み込み関数ではなくなります.
では例をあげてみましょう.Cソースの中にアセンブラの命令を入れています.
main()
{
...............
asm("movl %esp,%ebp");
...............
}
上記のソースは非ANSIです.ANSIに限定したい場合,asm()を__asm__()にしなくてはなりません.
GCCの拡張機能として非ANSIのものはいくつかありますが,当然のことながら-ansiを指定した場合には使用できなくなってしまいます.もっとも,拡張機能に慣れてしまった場合,ほかのC言語を使用するときに大きな障害となってしまうので,なるべく使用しないほうが良いと思います.GNU Cの拡張機能の説明は後述します.
● -flang-isoc9x
C9X規格にある機能のサポートを有効にします.「ISO/IEC 9899 : 1999 - Programming Language C」(略称:C99)規格の新機能はたくさんありますが,この詳しい説明も後述します.
● -fstd=
言語規格を明示的に指定します.このオプションに指定できる値は以下のとおりです.
・iso9899:1990 -ansiに同じ
・iso9899:199409補遺1で修正されたISO C
・iso9899:199x ISO C 9x
・c89 -std=iso9899:1990に同じ
・c9x -std=iso9899:199xに同じ
・gnu89 iso9899:1990+gnu拡張であり,デフォルト
・gnu9x iso9899:199x+gnu拡張
このオプションが指定されていない場合でも,新しい規格のいくつかの機能を使うことが可能です.たとえば,-fstd=c9xが指定されていない場合でも,そこで決定された規格を使ってもかまいません.
● -fno-asm
asm,inline,typeofをキーワードとして認識しないようにします.これらの名前は識別子として使用可能になり,代わりに__asm__,__inline__,__typeof__がキーワードとして使えるようになります.-ansiを指定すると,-fno-asmも有効になります.
では,例をあげてみましょう.渡された二つの数値を交換しています.
inline swap(int x,int y)
{
int temp;
temp = y;
y = x;
x = temp;
}
上記のソースは-fno-asmを指定した場合には,次のようになります.
__inline__ swap(int x,int y)
{
int temp;
temp = y;
y = x;
x = temp;
}
● -fno-builtin
名前の先頭が__builtin__で始まらない組み込み関数を認識しません.影響を受ける関数には,abort,abs,alloca,cos,exit,fabs,ffs,labs,memcmp,memcpy,sin,
sqrt,strcmp,strcpy,strlenがあります.
通常,GCCは特定の組み込み関数をより効率的に処理するために特殊なコードを生成します.たとえばallocaの呼び出しは,スタックを直接調整する単一の命令になることがあります.
また,memcpyの呼び出しは,コピー命令のループをインライン展開したものになることがあります.結果として生成されるコードは,多くの場合,より小さくかつより速いものになりますが,関数を呼び出している部分が,実際にはもはや関数呼び出しには見えなくなるため,その関数呼び出しに対してブレークポイントを設定することができなくなるうえ,異なるライブラリとリンクすることによって関数のふるまいを変更することもできなくなります.
-ansiオプションを指定すると,allocaとffsは組み込み関数ではなくなります.これらの関数はANSI規格では規定されていないからです.
● -fhosted
コンパイル処理はホスト環境で実行されるものと断定します.これは,暗黙のうちに-fbuiltinを指定します.ホスト環境とは,標準ライブラリ全体が利用可能で,かつmainがint型の戻り値を持つ環境のことです.カーネルを除外すれば,ほとんどすべての環境がこれに当てはまります.このオプションは,-fno-freestandingと同等です.
コンパイル処理は孤立した環境で実行されるものと断定します.これは暗黙のうちに-fno-builtinを指定します.孤立した環境とは,標準ライブラリが存在しない可能性があり,かつプログラムが必ずしもmainから開始されるとは限らない環境のことです.最も明白な例はOSのカーネルです.このオプションは,-fno-hostedと同等です.
● -trigraphs
ANSI Cの3文字表記(trigraph)をサポートします.実用的とは思えないので使用しないほうが無難でしょう.
-ansiオプションは,暗黙のうちに-trigraphsを指定します.trigraphは二つの疑問符(??)から始まる3文字のシーケンスです.
● -traditional
旧来のCコンパイラの機能をサポートします.例として,以下のような事例が挙げられます.
・すべてのextern宣言が,関数定義の中で書かれているものであっても,グローバルとして扱う
・新しい予約語である,typeof,inline,signed,const,volatileを認識しないことにする
・ポインタと整数の比較を常に許す
・整数型unsigned shortとunsigned charを,unsigned intに格上げする
・許される範囲外の浮動小数点数リテラルがエラーとならないようにする
・ANSI Cでは1個の不正な前処理トークンとしての数として認識する構文,たとえば0xe-0xdを,代わりに式として取り扱う
・文字列「定数」は必ずしも定数とは限らない.書き込み可能な空間に配置され,同じ値を持つ定数は別途確保される
・register宣言されていない自動変数はすべてlongjmpにより保存される.通常,GNU CはANSI Cに従う.つまり,volatile宣言されていない自動変数は,破壊される可能性がある
・文字エスケープ列エxとエaは,それぞれリテラル文字,xとaとして評価される.-traditionalを指定しない場合,エxは,ある文字の16進表現の接頭辞となり,エaはベルを生成する
・コメントは空白に変換されるのではなく,完全に消去される.これにより,古くから行われているトークンの連結ができるようになる
・前処理の指示子の中の#シンボルは,行の先頭の文字でなければならない
・マクロ引き数は,マクロ定義内の文字列定数の中でも認識される.プリプロセッサは,常に文字列定数は改行をもって終了するものとみなす
・-traditionalを使うと,事前定義マクロ__STDC__は定義されないが,__GNUC__は定義される
しかし,試してみればわかりますが,このオプションをつけると現在ではヘッダでエラーが起きます.そしてソースの可読性,可搬性を高める意味でもこのようなオプションは使用しないほうが良いと思います.
● -fcond-mismatch
3項演算子式で,2番目と3番目の引き数の型が違っても許します.その場合,式の値はvoidにします.
リスト3はその例です.3項演算子の二番目の引き数がintを返すprintfなのに,三番目の引き数はchar*を戻すstrcpyになっています.この場合,このオプションをつければワーニングが出ません.
〔リスト3〕-fcond-mismatchを使う例
|
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, char* argv[])
{
char a = -1;
char buf[255];
a?(void*)printf("true"):(void*)strcpy(buf,"false");
return 0;
}
|
|
● -funsigned-char
これは,ソース中に記述されたcharをすべてunsigned charとして扱うようにするオプションです.一時的にそうしたいならともかく,普通はソースの可読性,可搬性を高める意味でもこのようなオプションは使用しないほうが良いと思います.
リスト4はその例です.実行結果は以下のようになります.
$ gcc -O0 -o test11 test11.c
$ ./test11
-1
$ gcc -funsigned-char -O0 -o test11 test11.c
$ ./test11
255
〔リスト4〕-funsigned-charを使う例(test11.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char a;
a = 255;
printf("%d\n",a);
return 0;
}
|
|
● -fsigned-char
これは先に説明した-funsigned-charの逆です.同じくこのオプションを使用するときには注意が必要です(リスト5).
実行結果は次のようになります.
$ gcc -funsigned-char -O0 -o test12 test12.c
$ ./test12
-1
〔リスト5〕-fsigned-charを使う例(test12.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
signed char a;
a = 255;
printf("%d\n",a);
return 0;
}
|
|
● -fsigned-bitfields,-funsigned-bitfields,
-fno-signed-bitfields,-fno-unsigned-bitfields
これらのオプションは,ビットフィールドの宣言にsignedもunsignedも使っていないときに,そのビットフィールドが符号付きとなるか符号無しとなるかを決定します.
リスト6はその例です.
〔リスト6〕ビットフィールドの符号の検証(test13.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct test_bit
{
char a : 4;
char b : 5;
char c : 7;
char d : 8;
} test;
int main(int argc, char* argv[])
{
test.a = 2;
test.b = 31;
test.c = 0;
test.d = 255;
printf("%x\n",test);
return 0;
}
|
|
$ gcc -funsigned-bitfields -O0 -S test13.c
このときに出力されるアセンブラコードはリスト7です.
〔リスト7〕ビットフィールドが符号なしの場合
|
.file "test13.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "%x\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
andb $-16,test
orb $2,test
orb $31,test+1
andb $-128,test+2
movb $255,test+3
addl $-8,%esp
movl test,%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
xorl %eax,%eax
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.comm test,4,1
.ident "GCC: (GNU)
2.95.3 20010315 (release)"
|
|
$ gcc -fsigned-bitfields -O0 -S test13.c
このときに出力されるアセンブラコードはリスト8です.
〔リスト8〕ビットフィールドが符号ありの場合
|
.file "test13.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "%x\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
andb $-16,test
orb $2,test
orb $31,test+1
andb $-128,test+2
movb $-1,test+3
addl $-8,%esp
movl test,%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
xorl %eax,%eax
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.comm test,4,1
.ident "GCC: (GNU)
2.95.3 20010315 (release)"
|
|
ビットフィールドが符号なしの場合,「test+3」のアドレスにCソースで記述したとおりに255をセットしていますが,ビットフィールドが符号ありの場合,−1をセットしています.
● -fwritable-strings
文字列定数を書き込み可能なデータセグメントに格納します.これは古いタイプのCで記述されたプログラムと互換を取るためにあります.使用する前に可読性・可搬性のことを考えてみましょう.
リスト9に例を示します.
〔リスト9〕-fwritable-stringsの検証(test14.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char moji[] = "abcdefghijk ";
moji[5] = 'g';
printf("%s\n",moji);
return 0;
}
|
|
$ gcc -fwritable-strings -O0 -S test14.c
このときに出力されるアセンブラコードをリスト10に示します.
〔リスト10〕文字列定数を書き込み可能にした場合
|
.file "test14.c"
.version "01.01"
gcc2_compiled.:
.data
.LC0:
.string "abcdefghijk "
.LC1:
.string "%s\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
leal -16(%ebp),%edx
movl $.LC0,%eax
movl (%eax),%edx
movl %edx,-16(%ebp)
movl 4(%eax),%edx
movl %edx,-12(%ebp)
movl 8(%eax),%edx
movl %edx,-8(%ebp)
movl 12(%eax),%eax
movl %eax,-4(%ebp)
movb $103,-11(%ebp)
addl $-8,%esp
leal -16(%ebp),%eax
pushl %eax
pushl $.LC1
call printf
addl $16,%esp
xorl %eax,%eax
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU)
2.95.3 20010315 (release)"
|
|
$ gcc -O0 -S test14.c
このときに出力されるアセンブラコードをリスト11に示します.
〔リスト11〕文字列定数を書き込み可能にしない場合
|
.file "test14.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "abcdefghijk "
.LC1:
.string "%s\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
leal -16(%ebp),%edx
movl $.LC0,%eax
movl (%eax),%edx
movl %edx,-16(%ebp)
movl 4(%eax),%edx
movl %edx,-12(%ebp)
movl 8(%eax),%edx
movl %edx,-8(%ebp)
movl 12(%eax),%eax
movl %eax,-4(%ebp)
movb $103,-11(%ebp)
addl $-8,%esp
leal -16(%ebp),%eax
pushl %eax
pushl $.LC1
call printf
addl $16,%esp
xorl %eax,%eax
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU)
2.95.3 20010315 (release)"
|
|
アセンブラのソースを見ると,-fwritable-stringsをつけてコンパイルしたものは,データセグメントに文字列定数が格納されています.
● -fallow-single-precision
-traditionalを指定してコンパイルする場合でも,単精度の数学演算を倍精度に拡張しません.ANSI CやGNU Cの仕様にしたがってコンパイルする場合には,まったく意味がありません.
|