● コメントの形式
C++ではコメントを次のように記述します.
#include <stdio.h>
//typedef short t_id; //コメント行
int foo(t_id x);
int main(void)
もちろんオプションに-ansiまたは-traditionalを指定した場合には,C++方式のコメントは認識されません.ほかの多くのCの実装でもこのようなコメントを使うことができるので,ソースをANSIに限定しなくてよいのなら,このコメント形式を使って問題ないでしょう(リスト7).
〔リスト7〕C++方式のコメントを使用したソース(test88.c)
|
#include <stdio.h>
//コメント
typedef short t_id;
int foo(t_id x);
int main(void)
{
int result;
t_id y;
printf("start\n");
y = 2;
result = foo(y);
printf("%d\n",result);
return 0;
}
int foo(t_id x)
{
return x * x;
}
|
|
-ansiオプションでコンパイルした場合,次のようにエラーとなります.
$ gcc -S -ansi test88.c
test88.c:2: parse error before '/'
test88.c:4: parse error before 'x'
test88.c: In function 'main':
test88.c:8: 't_id' undeclared (first use in this function)
test88.c:8: (Each undeclared identifier is reported only once
test88.c:8: for each function it appears in.)
test88.c:8: parse error before 'y'
test88.c:10: 'y' undeclared (first use in this function)
test88.c: At top level:
test88.c:16: parse error before 'x'
test88.c: In function 'foo':
test88.c:18: 'x' undeclared (first use in this function)
$
● 識別子の名前の中のドル記号
GNU Cでは識別子の名前の中でドル記号($)を使うことができます.もちろん古いCコンパイラでもそのような識別子を使うことを許しているものもあります.しかし,識別子の中のドル記号がサポートされないターゲットマシンもあります.その理由として,ターゲットマシンのアセンブラが識別子の中のドル記号を許さないことがあるからです.
そのようなターゲットマシン用にクロスコンパイルをする場合には,オプション-pedanticをつけてコンパイルするとエラーになるのでわかりやすいでしょう(リスト8).
〔リスト8〕識別子の名前にドル記号を使用したソース(test89.c)
|
#include <stdio.h>
typedef short t_id;
int foo(t_id x);
int main(void)
{
int $result;
t_id y;
printf("start\n");
y = 2;
$result = foo(y);
printf("%d\n",$result);
return 0;
}
int foo(t_id x)
{
return x * x;
}
|
|
-pedanticオプションでコンパイルした場合,次のようにエラーとなります.なお連載第3回(本誌2002年10月号)の「警告を要求/抑止するオプション」でpedanticオプションについて説明しています.
$ gcc -S -pedantic test89.c
test89.c:6: warning: '$' in identifier
test89.c:10: warning: '$' in identifier
test89.c:11: warning: '$' in identifier
test89.c: In function 'main':
test89.c:6: warning: '$' in identifier
test89.c:10: warning: '$' in identifier
test89.c:11: warning: '$' in identifier
$
● ESC文字について
メールの文字列中などで,[ESC]文字と$Bや(Bなどの文字を組み合わせた「エスケープシーケンス」で,文字セットを切り替えています.JIS-1983規格に切り替える際には[ESC] $ Bを文字列中に埋め込みます.
そのような用途に使う場合に,文字列リテラルとして“\e$B”のように定義ができます(リスト9).
〔リスト9〕ESC文字を使用したソース(test90.c)
|
#include <stdio.h>
int main(void)
{
printf("\e$B");
printf("\e$(O");
return 0;
}
|
|
$ gcc -S -pedantic test90.c
test90.c: In function 'main':
test90.c:4: warning: non-ANSI-standard escape sequence, '\e'
test90.c:5: warning: non-ANSI-standard escape sequence, '\e'
$
拡張機能を使用した場合にワーニングを出す指定をすると,上記のようになります.
● 型あるいは変数のアラインメントを問い合わせる
たとえば,sizeofを使う場合と同様に__alignof__で,あるオブジェクトがどのようにアラインメントされるかを問い合わせることができます.実行環境によっては,アラインメントを必要としないものもあります.そのような場合に__alignof__は型の推奨アラインメントを報告します.
__alignof__のオペランドがlvalueの場合には,__alignof__の値はそのlvalueが取るとわかっているアラインメントの値のうち最大の値となります.
このアラインメントの値は,そのlvalueのデータ型から決められることもありますし,そのlvalueが構造体の一部である場合には,その構造体からアラインメントの値を継承することもあります(リスト10,リスト11).
上記のソースの実行結果は,次のようになります.
$ gcc -o test91 test91.c
$ ./test91
doubleの境界----8
floatの境界----4
long longの境界----8
intの境界----4
charの境界----1
fooの境界----8
foo.eの境界----1
$
おそらく身近にあるインテルまたはインテル互換ではない環境,たとえばMac OS X上のGCCでは,また違った結果になると思います.
ちなみに第5回連載で作成したSHプロセッサ向けの環境でコンパイルすると,次のようになります(リスト12).
〔リスト12〕test91.cをSHプロセッサ用にコンパイルした際のアセンブラ(SH_test91.s)
|
.file "test.c"
.data
gcc2_compiled.:
___gnu_compiled_c:
.text
.align 2
LC0:
.ascii "double\244\316\266\255\263\246----%d\12\0"
.align 2
LC1:
.ascii "float\244\316\266\255\263\246----%d\12\0"
.align 2
LC2:
.ascii "long long\244\316\266\255\263\246----%d\12\0"
.align 2
LC3:
.ascii "int\244\316\266\255\263\246----%d\12\0"
.align 2
LC4:
.ascii "char\244\316\266\255\263\246----%d\12\0"
.align 2
LC5:
.ascii "foo\244\316\266\255\263\246----%d\12\0"
.align 2
LC6:
.ascii "foo.e\244\316\266\255\263\246----%d\12\0"
.align 2
.global _main
_main:
mov.l r8,@-r15
mov.l L3,r1
mov.l r14,@-r15
sts.l pr,@-r15
jsr @r1
mov r15,r14
mov.l L4,r8
mov.l L5,r4
jsr @r8
mov #4,r5
mov.l L6,r4
jsr @r8
mov #4,r5
mov.l L7,r4
jsr @r8
mov #4,r5
mov.l L8,r4
jsr @r8
mov #4,r5
mov.l L9,r4
jsr @r8
mov #1,r5
mov.l L10,r4
jsr @r8
mov #4,r5
mov.l L11,r4
jsr @r8
mov #1,r5
mov #0,r0
mov r14,r15
lds.l @r15+,pr
mov.l @r15+,r14
rts
mov.l @r15+,r8
L12:
.align 2
L3:
.long ___main
L4:
.long _printf
L5:
.long LC0
L6:
.long LC1
L7:
.long LC2
L8:
.long LC3
L9:
.long LC4
L10:
.long LC5
L11:
.long LC6
|
|
$ sh-hitachi-coff-gcc -O3 -S test91.c
上記のようにchar,foo.eは1バイト,その他は4バイト境界になっています.
● 変数の属性の指定
キーワード__attribute__により,変数または構造体フィールドに特別な属性を指定することができます.このキーワードの後に,二重の丸括弧(())に囲まれた属性指定が続きます.現在,8個の属性aligned,mode,nocommon,packed,section,transparent_union,unused,weakが変数に対して定義されています.その他の属性が,前述の関数および後述する型に対して定義されています.
複数の属性を指定するには,たとえば“__attribute__ ((aligned (16), packed))”のように,2重の丸括弧(())の中で属性をカンマで区切ります.
それぞれのキーワードの前後に__を付けて属性を指定することもできます.もし同じ名前をもつマクロが定義済みでも,ヘッダファイルの中でキーワードを使うことができるようになります.たとえば,alignedの代わりに__aligned__を使うことができます.
○aligned (alignment)
この属性は,変数または構造体フィールドで最小のアラインメントの値をバイト単位で指定します.以下のソースはそれぞれアラインメントを16バイト,32バイトにしたものです.
オブジェクトファイルのシンボルをnmコマンドで出力してみます.すると,リスト13〜リスト16のようにfoo1のアラインメントが変わっています.
なお,aligned属性の指定においてアラインメントの係数を省略した場合,コンパイルのターゲットマシン上においてもっとも効率のよい値をコンパイラが自動的に設定します.インテルx86アーキテクチャの場合は,リスト17のようにintの場合は4が設定されます.
〔リスト17〕変数のアラインメントを自動的に設定したソース(test94.c)
|
#include <stdio.h>
//変数属性の指定 aligned (alignment)
struct foo { int x[20] __attribute__ ((aligned )); }foo1;
int main(void)
{
foo1.x[0] = 1;
foo1.x[1] = 2;
printf("foo1の境界----%d\n",__alignof__(foo1));
return 0;
}
|
|
以下は実行結果です.
$ gcc -o test94 test94.c
$ ./test94
foo1の境界----4
$
○mode (mode)
この属性は,宣言のデータ型を指定します.指定される型は,モードmodeに対応する型です.byteを指定すれば1バイトになります.wordを指定すればその環境の1ワード,pointerを指定すればその環境で使用できるポインタの長さが確保されます(リスト18).
〔リスト18〕mode属性で変数の長さを変更しているソース(test95.c)
|
#include <stdio.h>
//変数属性の指定 mode (mode)
int main(void)
{
char x __attribute__ ((mode(pointer) ));
int y __attribute__ ((mode(byte) ));
printf("xのサイズ----%d\n",__alignof__(x));
printf("yのサイズ----%d\n",__alignof__(y));
return 0;
}
|
|
実行結果は次のようになります.
$ gcc -o test95 test95.c
$ ./test95
xのサイズ----4
yのサイズ----1
$
リスト18ではchar型にポインタの長さを,int型に1バイトを指定しています.プロセッサの環境が変わってもポインタの長さを正確に確保したり,1ワードの大きさを正確に確保する場合に有効な方法ですが,GCCに慣れていない人には理解できないでしょう.
○nocommon
この属性を指定するとグローバル変数の場合,.bssセクションに配置します(リスト19,リスト20).なお,その変数は初期化されます.
○packed
packed属性は,aligned属性が指定されていない限り,変数または構造体フィールドが,アラインメントを可能な限り最小の値にすることを指定します.この最小値は,変数については1バイトであり,フィールドについては1ビットです(リスト21,リスト22).
実行結果は次のようになります.
$ ./test97
foo1のサイズ----9
foo1.xのサイズ----8
foo1_のサイズ----12
foo1_.xのサイズ----8
$
○section (“section-name”)
コンパイルの際に,通常は生成するオブジェクトをdataやbssといったセクションに置きます.しかし,場合によっては,たとえば特殊なハードウェアにマップするために,追加のセクションが必要になったり,特定の変数を特殊なセクションに置くことが必要になります.
section属性は,ある変数が,ある特定のセクション内に存在するよう指定します(リスト23,リスト24).
このように,それぞれfoo1_section,foo2_section,data_sectionに置かれています.
オブジェクト上ではリスト25のように配置されます.
〔リスト25〕section属性を指定したソースをリンクしたオブジェクト配置リスト(test98nm.txt)
|
08048304 t Letext
080494ac ? _DYNAMIC
08049490 ? _GLOBAL_OFFSET_TABLE_
08048450 R _IO_stdin_used
08049484 ? __CTOR_END__
08049480 ? __CTOR_LIST__
0804948c ? __DTOR_END__
08049488 ? __DTOR_LIST__
0804947c ? __EH_FRAME_BEGIN__
0804947c ? __FRAME_END__
0804954c A __bss_start
08049454 D __data_start
w __deregister_frame_info@@GLIBC_2.0
080483f0 t __do_global_ctors_aux
08048330 t __do_global_dtors_aux
w __gmon_start__
U __libc_start_main@@GLIBC_2.0
w __register_frame_info@@GLIBC_2.0
08049478 A __start_data_section
08049460 A __start_foo1_section
0804946c A __start_foo2_section
0804947c A __stop_data_section
0804946c A __stop_foo1_section
08049478 A __stop_foo2_section
0804954c A _edata
08049564 A _end
08048430 ? _fini
0804844c R _fp_hw
08048274 ? _init
080482e0 T _start
08048304 t call_gmon_start
0804945c d completed.4
08049478 ? data
08049454 W data_start
08048384 t fini_dummy
0804946c ? foo2_
08049460 ? foo_
08049460 d force_to_data
08049460 d force_to_data
08048390 t frame_dummy
08048304 t gcc2_compiled.
08048330 t gcc2_compiled.
080483f0 t gcc2_compiled.
08048430 t gcc2_compiled.
080483d0 t gcc2_compiled.
080483b8 t init_dummy
08048418 t init_dummy
080483d0 T main
0804954c b object.11
08049458 d p.3
|
|
○transparent_union
この属性は共用体型の関数パラメータに対して指定されます.そのパラメータに対応する引き数自体は,その共用体の任意のメンバの型をもつことができます.しかし,その引き数がその関数に渡される際には,共用体の1番目のメンバの型をもつものとして扱われます.詳細については,
型属性の指定で説明します.
○unused
この属性は変数に対して指定されます,その変数はおそらく使われないはずであるということを意味します.GNU Cは,その変数については警告メッセージを出力しません.
○weak
weak属性については,第7回(本誌2003年3月号)の連載にある関数属性の宣言で説明しています.
○model (model-name)
model属性はオブジェクトがスモール/ミディアム/ラージであると宣言しなくてはならない環境において,それを宣言するものです.
|