●
可変個数の引き数を受け取ることができるマクロ
#define eprintf(format, args...)
fprintf (stderr, format , ## args)
のようなマクロを定義した場合,eprintf(arg1,arg2,arg3,arg4)もeprintf(arg1,arg2)も正しく解釈されます.
● 左辺値ではない配列の添字が存在
左辺値ではない配列に対して添字を使うことができます.ただし,単項演算子‘&’は使えません.このような構文を使用すると,可読性や可搬性に問題が起きるので推奨できません.
● voidポインタと関数へのポインタの算術演算
GNU Cでは,voidポインタや関数へのポインタで加算と減算の演算をすることが可能です.これは,voidや関数のサイズを1とみなして計算されます.この結果は,void型と関数型でさらにsizeofが許され,1を返すということです.
オプション-Wpointer-arithは,この拡張が使われた場合に警告を出します.
やはり通常は混乱の元になるので,voidならば想定される型にキャストするなりして,この拡張仕様は回避すべきです.
● 非定数による初期化
標準のCと同様に,GNU Cでも自動変数に割り当てられた集合体の初期化子の要素が,定数式である必要はありません(リスト8〜リスト12).
生成された関数tblを見るとわかるように,最適化されると,できるかぎり定数による初期化を試みます.生成されるコードは,定数による初期化のほうが効率よく高速に動作します.この記法は標準のCでも使われているので,可搬性に関しては問題ないと思います.場合に応じて使用してください.
● 生成関数式
GNU Cは,生成関数式「constructor expression」を使うことができます.
GCCのマニュアルには,次のような記述があります.
宣言
struct foo {int a; char b[2];} structure;
生成関数式によってstruct fooを生成するコード(1)
structure = ((struct foo) {x + y, 'a', 0});
生成関数式によってstruct fooを生成するコード(2)
{
struct foo temp = {x + y, 'a', 0};
structure = temp;
}
GCCのマニュアルには,コード(1)とコード(2)は同等であると書かれていますが,実際には生成されたアセンブラを見るとわかるように,(1)のほうが少しコンパクトになります(リスト13〜リスト17).
結果は,コード(1),すなわち生成関数式を使い最適化したもの,コード(1)で最適化しないもの,コード(2)すなわちANSI C準拠で書いたものの順でコンパクトになりました.
この機能を使って可搬性に問題が起こるとすれば,C++のクラスを使うべきだと思います.しかし,速度を上げ,実行形式も小さくしたいのであれば,この拡張機能を使用すべきでしょう.
● 配列の初期化について
ANSI Cでは,配列を初期化する際には,次のようにしなくてはなりません.
int tbl[5]={0,0,3,4,10};
しかし,拡張仕様では,次のような記述が認められます.
int tbl[5]={[4]=10,[2]3,[3]4};
すなわち,4番目の要素に10をセットし,2番目の要素に3をセットし3番目の要素に4をセットしています.
実際にコードを書いてみます(リスト18,リスト19).
リストのようにANSI C形式で記述したものは,アセンブラ上でも0,0,3,4,10と配置するコードに生成されますが,この拡張仕様を使えば0が2要素,3,4,10と生成されています.
この拡張機能を使うとコンパクトかつ速いコードが生成されるはずです.
なお,リスト20,リスト21のような形式でも記述できます.生成されるアセンブラコードは同じですが,Cソースの可読性は高くなると思います.
また,構造体の配列を初期化する際にも今までと違った方法でできます(リスト22,リスト23).リストからわかるように,構造体中のどの要素に値をセットするかが明確になります.
なお,共用体の配列を初期化する際にも,リスト24に示すような,わかりやすい方法で行うことが可能です.