C/C++言語初心者はこのページを読まないでください。
このページではgcc独自のC/C++拡張文法について解説します。 これらの拡張文法が可能にする機構は確かに便利なのですが、 もちろんANSI規格に従っていないので、一般的には使うべきではありません。
C/C++言語文法を学び始めている初心者はこれらgcc拡張文法を
知るべきではありません。C/C++言語を正しく理解する上で大きな
支障となります。
C/C++言語を十分に熟知した者は、gccがこのようなこともすることを
「雑談」として知っておくと楽しいかもしれません。もちろん
実戦に使うべきではありませんが。しかし初心者が偶然に、これらの
機能を使ってうまくいく場合がありますので、そのような初心者を
見つけたら、それが標準規格ではないことを注意してください。
gccでは同型で同じサイズの配列変数間に限り、 次のようにして配列変数の全要素をコピーすることができます。
しかし gccの提供するinfoにはこの機能についての説明がありません。int a[8], b[8]="StringB"; a = b; // GCC Extension?
標準規格では配列の初期化で指定できる値は定数ですが、
gcc拡張文法では変数を指定することができます。
int a,b;に対して次のように配列変数を初期化することができます。
標準規格で実装するには、宣言後に個別に代入することです。int n[3] = { a+b, a-b, a*b }; // GCC Extension
標準規格では配列の宣言時に与える配列のサイズは定数でですが、
gcc拡張文法では変数でサイズを指定することができます。
次のように配列のサイズを実行時に決定できます。
標準規格で実装するには、newを使うことです。int n; scanf("%d", &n ); int a[n]; // GCC Extension
標準規格ではswitch case文のcaseに指定する値はただ一つですが、
gcc拡張文法ではPascalのように値の範囲を指定することができます。
int n;に対して次のようにswitch case文を記述することができます。
標準規格で実装するには、if文を多用することです。switch( n ){ case 0 ... 4 : /* do something */ break; // GCC Extension case 5 ... 9 : /* do something */ break; // GCC Extension }
標準規格ではアドレス値は足し引き算の対象ではありませんが、
gcc拡張文法では一般化された左辺値の機能とキャスト演算子を
使ってアドレス値の足し引き算をすることができます。
char* ptr; int add;に対して次のようにアドレス値の足し算が
できます。
標準規格で実装するには、(int)ptr += add; // GCC Extension
とすることです。(int)(ptr = (char *)(int) ((int)ptr + add));
標準規格では小括弧()内に単純な式をカンマで並べて、その最後の項が
小括弧()の値を表しますが、
gcc拡張文法では小括弧()内に中括弧{}でまとめた複文を入れることができます。
この複文中ではループや変数宣言もできます。最後の項がやはり小括弧()
の値を表します。
max()の#defineマクロを次のように作ることができます。
引数の値を一時的な変数に保管して、それの大小を評価しているので、 よくある#defineマクロの落し穴に落ちません。#define max(a,b) ( \ { \ // GCC Extension int _a = (a), _b = (b); \ _a > _b ? _a : _b; \ } \ // GCC Extension )
標準規格で実装するには、inline関数を使うことです。 そのほうが遥かに簡単で安全です。
gcc拡張文法では typedef で型を表す変数のようなものを作ることができます。
次のようにすると、ty型は変数xと同じ型(つまりint型)になり、
その型で変数yが宣言されます。
この機能を先のmax()の#defineマクロに利用すると有用でしょう。int x; typedef ty = x; // GCC Extension ty y; // GCC Extension
これで任意の型の変数同士のmax()マクロができます。 (>オペレータが適切に定義されているとして。)#define max(a,b) ( \ { \ // GCC Extension typedef _ta = (a), _tb = (b); \ // GCC Extension _ta _a = (a); _tb _b = (b); \ // GCC Extension _a > _b ? _a : _b; \ } \ // GCC Extension )
標準規格で実装するには、templateを次のように使うことです。
ただしこれはinline展開できません。template <class T> T max( T a, T b ){ return (a>b) ? a : b; }
gcc拡張文法ではtypeof演算子で変数の型を表すことができ、
さらにそれで別の変数を宣言することができます。
次のようにすると、変数yは変数xの型で宣言されます。
この機能を先のmax()の#defineマクロに利用すると有用でしょう。int x; typeof(x) y; // GCC Extension
#define max(a,b) ( \ { \ // GCC Extension typeof(a) _a; \ // GCC Extension typeof(b) _b; \ // GCC Extension _a = (a); _b = (b); \ _a > _b ? _a : _b; \ } \ // GCC Extension )
標準規格で実装するには、先のようにtemplateを使うことです。
gcc拡張文法でのC++では特別な演算子 >? と <? が使えます。
>? は a >? b として、大きい方を表し、
<? は a <? b として、小さい方を表します。
汎用のmax()マクロとmin()マクロは次のように作られます。
#defineマクロの落し穴に落ちることもありません。 標準規格で実装するには、やはりtemplateです。#define max(a,b) ((a) >? (b)) // GCC Extension of C++ #define min(a,b) ((a) <? (b)) // GCC Extension of C++
標準規格では条件演算子の文法は
条件式 ? 真の場合の式 : 偽の場合の式ですが、
正常ならFILEポインタを返して、失敗ならperror()関数で 失敗の原因を報告してからNULLポインタを返します。FILE* myfopen( char* fname ) { return fopen(fname,"r") ? : (perror("fopen"),(FILE*)NULL); // GCC Extension }
標準規格で実装するには、一時的なFILEポインタ変数を 用意することです。
標準規格ではラベルの有効範囲はその関数内全域ですが、
gcc拡張文法では次のようにしてラベルを宣言することで、有効範囲を
その宣言を囲む中括弧内に限定することができます。
#defineマクロ内でgoto文を使う場合に、この限定されたラベルが 必要です。なぜなら同じ関数内でそのマクロを複数回呼び出すと、 標準規格ではそのラベルが衝突するからです。// GCC Extension __label__ retry;
標準規格で実装するには、inline関数を使うことです。#define INPUT() ( \ { \ // GCC Extension __label__ retry; \ // GCC Extension int _x; \ retry: \ printf("Input an integer="); \ if( scanf("%d", &x ) != 1 ){ \ rewind(stdin); \ goto retry; \ } \ x; \ } \ // GCC Extension )
gcc拡張文法ではラベルとgoto文を次のように使うことができます。
N88BASICの ON GOTO文のようなことができるわけです。 switchによる分岐と異なり変数の評価が無いので分岐が高速です。void dispatch( int i ) { static void *array[] = { &&job0, &&job1, &&job2 }; // GCC Extension goto *array[i]; // GCC Extension job0: printf("doing the job0.\n"); return; job1: printf("doing the job1.\n"); return; job2: printf("doing the job2.\n"); return; }
標準規格で実装するには、ラベルの代わりに関数ポインタ を関数ポインタ配列に入れておいて、gotoの代わりにその関数ポインタ 配列の要素が指す関数を実行することです。
gcc拡張文法でのC言語ではネストされた関数を作ることができます。 C++言語では使えません。
標準規格で同等の機能を実装することはできません。void plot( double x[], double y[], int num ) { int i; int X( double xd ){ /* GCC Extension of C */ return( (int)(2.0*xd) ); } int Y( double yd ){ /* GCC Extension of C */ return( (int)(4.0*yd) ); } for( i=0 ; i<num ; i++ ){ printf("%d %d\n", X(x[i]), Y(y[i]) ); } } #endif
gcc拡張文法でのC言語では多次元配列を次のようにして
関数に渡すことができます。C++言語では使えません。
標準規格で実装するには、特殊なclassを用意することです。void func( int len, int a[len][len] ) /* GCC Extension of C */ { int i, j; for( i=0; i<len; i++ ){ for( j=0; j<len; j++ ){ a[i][j] = i+j; } } } int a[4][4]; func( 4, a );
gcc拡張文法でのC言語では配列や構造体や共用体の変数の初期化で
次のようにして、代入先の要素を指定することができます。
C++言語では使えません。
標準規格で実装することはできません。/* GCC Extensions of C */ int a[6] = { [4] 29, [2] = 15 }; int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 }; struct point { int x, y; }; struct point p1 = { y: 1, x: 2 }; struct point p2 = { .y = 1, .x = 2 }; union foo { int i; double d; }; union foo f = { d: 4 }; int whitespace[256] = { [' '] = 1, ['\t'] = 1, ['\n'] = 1, ['\r'] = 1 };