● アセンブラコードの中で使われる名前の制御
Cの関数または変数に対してアセンブラコードの中で使われる名前を,以下のようにその宣言子の後にasm(または__asm__)キーワードを書くことによって,指定することができます(リスト30,リスト31).
このように,アセンブラ中での名前が変更されています.
注意点としては,変更後の名前がアセンブラ中で衝突しないようにすることです.そうなってしまった場合,当然のことながらコンパイルエラーにはなりません.
● 指定されたレジスタの中の変数
GNU Cでは,指定されたハードウェアレジスタの中に少数の広域変数を置くことができます.また,通常のレジスタ変数が割り当てられるべきレジスタを指定することもできます.しかし,めったに使わない機能だと思います.
1)広域レジスタ変数の定義
GNU Cではリスト32,リスト33のようにして,広域レジスタ変数を定義することができます.
問題は,プロセッサによってレジスタの名称が異なるため,環境に依存してしまうことです.それを理解して使用するのであれば,効率の良いコードを書くことができるでしょう.
2)局所変数に対するレジスタの指定
同じように局所変数もレジスタに割り当てることができます.宣言の場所が違うだけです.同じように環境に依存します(リスト34,リスト35).
● 代替キーワード
オプション-traditionalを使うと,特定のキーワードが利用できなくなります.オプション-ansiを使うと,別の特定のキーワードが利用できなくなります.
ANSI Cのプログラムや伝統的なCのプログラムも含むすべてのプログラムにおいて,利用可能でなければならない汎用的なヘッダファイルの中で,GNU Cの拡張機能やANSI Cの機能を使いたい場合に,これが問題になります.
キーワードasm,typeof,inlineは,-ansiを指定してコンパイルされるプログラムの中では問題があり,キーワードconst,volatile,signed,typeof,inlineは,-traditionalを指定してコンパイルされるプログラムの中では問題があります.
この問題を解決する方法は,問題のある個々のキーワードの前後に__を付けることです.たとえば,asmの代わりに__asm__を,constの代わりに__const__を,inlineの代わりに__inline__を使ってください.
これはGNU Cの拡張機能なので,ほかのコンパイラではエラーとなってしまいます.ほかのコンパイラでコンパイルを行いたいのであれば,代替キーワードをマクロとして定義して,慣習的なキーワードと置き換えることができます.それは,次のようになります.
#ifndef __GNUC__
#define __asm__ asm
#endif
-pedanticを指定すると,ほとんどのGNU C拡張機能に対して警告メッセージが出力されます.式の前に__extension__と書くことによって,ある式の中における警告メッセージの出力を防ぐことができます.__extension__にはこれ以外の作用はありません.
● 不完全なenum型
enumタグを,それがもつことのできる値を指定せずに定義することができます.これはプログラムの意味がわかりにくくなってしまうと思います.enumの処理とstructやunionの処理の一貫性がより高くなるという利点はありますが,あまり使わないほうがよいかもしれません.
● 関数名の文字列
カレントな関数の名前を値としてもつ二つの文字列変数があらかじめ定義されています.変数__FUNCTION__は,ソースコードの中に記述されたとおりの関数名です.一方,変数__PRETTY_FUNCTION__は,言語固有のスタイルに変更された関数名です.
C言語では,この二つは同じになります(リスト36).
〔リスト36〕関数名の文字列変数の例のCソース(test104.c)
|
#include <stdio.h>
//関数名の文字列
int main(void)
{
printf ("__FUNCTION__ = %s\n", __FUNCTION__);
printf ("__PRETTY_FUNCTION__ = %s\n", __PRETTY_FUNCTION__);
return 0;
}
|
|
$ gcc -o test104 test104.c
$ ./test104
__FUNCTION__ = main
__PRETTY_FUNCTION__ = main
$
● 関数の復帰アドレスとフレームアドレスの獲得
以下の関数を使うことで,復帰アドレス,フレームアドレスを取得できます.
○__builtin_return_address (level)
この関数は,実行中の関数の復帰アドレス,または,実行中の関数を呼び出すまでに,途中で呼び出されてきた関数の中の一つの復帰アドレスを返します.
引き数levelは,呼び出しスタック中においてさかのぼるべきフレームの数です.値0を指定すると,実行中の関数の復帰アドレスが返ってきます.値1を指定すると,実行中の関数を呼び出した関数の復帰アドレスが返ってきます.以下,同様です.
引き数levelは整数の定数でなければなりません.マシンの中には,実行中の関数以外の関数の復帰アドレスを決定することが不可能なものがあります.そのような場合,あるいは,スタックのトップに達してしまった場合には,この関数は0を返します.この関数をデバッグの目的で使う際には,引き数には0以外の値だけを指定するべきです.
○__builtin_frame_address (level)
この関数は,__builtin_return_addressに似ていますが,関数の復帰アドレスではなく関数フレームのアドレスを返します.値0を指定して__builtin_frame_addressを呼び出すと,実行中の関数のフレームアドレスが返ってきます.値1を指定すると,実行中の関数を呼び出した関数のフレームアドレスが返ってきます.以下,同様です.
フレームとは,局所変数や待避されたレジスタを保持している,スタック上の領域のことです.通常,フレームアドレスとは,関数によって最初にスタックにプッシュされたワードのアドレスのことです.しかし,正確な定義は,プロセッサと呼び出し規約に依存します.プロセッサが専用のフレームポインタレジスタをもつ場合,関
数がフレームをもっていると,__builtin_frame_addressはフレームポインタレジスタの値を返します.__builtin_return_addressにあてはまる注意事項は,この関数にもあてはまります.
リンカの出力するオブジェクト配置リストなどを使用し,デバッグのためにこの関数を使用することで,効率の良いデバッグが可能になります.
おわりに
さて,拡張機能についてはこれで終わりです.
次回は「ISO/IEC 9899:1999 ―― Programming Language C」(略称:C99)規格について,説明と検証を行う予定です.
参考文献
1)GCCマニュアル,Free Software Foundation
|