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拡張文法では変数でサイズを指定することができます。
次のように配列のサイズを実行時に決定できます。
int n;
scanf("%d", &n );
int a[n]; // GCC Extension
標準規格で実装するには、newを使うことです。
標準規格ではswitch case文のcaseに指定する値はただ一つですが、
gcc拡張文法ではPascalのように値の範囲を指定することができます。
int n;に対して次のようにswitch case文を記述することができます。
switch( n ){
case 0 ... 4 : /* do something */ break; // GCC Extension
case 5 ... 9 : /* do something */ break; // GCC Extension
}
標準規格で実装するには、if文を多用することです。
標準規格ではアドレス値は足し引き算の対象ではありませんが、
gcc拡張文法では一般化された左辺値の機能とキャスト演算子を
使ってアドレス値の足し引き算をすることができます。
char* ptr; int add;に対して次のようにアドレス値の足し算が
できます。
標準規格で実装するには、(int)ptr += add; // GCC Extension
とすることです。(int)(ptr = (char *)(int) ((int)ptr + add));
標準規格では小括弧()内に単純な式をカンマで並べて、その最後の項が
小括弧()の値を表しますが、
gcc拡張文法では小括弧()内に中括弧{}でまとめた複文を入れることができます。
この複文中ではループや変数宣言もできます。最後の項がやはり小括弧()
の値を表します。
max()の#defineマクロを次のように作ることができます。
#define max(a,b) ( \
{ \ // GCC Extension
int _a = (a), _b = (b); \
_a > _b ? _a : _b; \
} \ // GCC Extension
)
引数の値を一時的な変数に保管して、それの大小を評価しているので、
よくある#defineマクロの落し穴に落ちません。
標準規格で実装するには、inline関数を使うことです。 そのほうが遥かに簡単で安全です。
gcc拡張文法では typedef で型を表す変数のようなものを作ることができます。
次のようにすると、ty型は変数xと同じ型(つまりint型)になり、
その型で変数yが宣言されます。
この機能を先のmax()の#defineマクロに利用すると有用でしょう。int x; typedef ty = x; // GCC Extension ty y; // GCC Extension
#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
)
これで任意の型の変数同士のmax()マクロができます。
(>オペレータが適切に定義されているとして。)
標準規格で実装するには、templateを次のように使うことです。
template <class T> T max( T a, T b ){
return (a>b) ? a : b;
}
ただしこれはinline展開できません。
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* myfopen( char* fname )
{
return fopen(fname,"r") ? : (perror("fopen"),(FILE*)NULL); // GCC Extension
}
正常ならFILEポインタを返して、失敗ならperror()関数で
失敗の原因を報告してからNULLポインタを返します。
標準規格で実装するには、一時的なFILEポインタ変数を 用意することです。
標準規格ではラベルの有効範囲はその関数内全域ですが、
gcc拡張文法では次のようにしてラベルを宣言することで、有効範囲を
その宣言を囲む中括弧内に限定することができます。
#defineマクロ内でgoto文を使う場合に、この限定されたラベルが 必要です。なぜなら同じ関数内でそのマクロを複数回呼び出すと、 標準規格ではそのラベルが衝突するからです。// GCC Extension __label__ retry;
#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
)
標準規格で実装するには、inline関数を使うことです。
gcc拡張文法ではラベルとgoto文を次のように使うことができます。
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;
}
N88BASICの ON GOTO文のようなことができるわけです。
switchによる分岐と異なり変数の評価が無いので分岐が高速です。
標準規格で実装するには、ラベルの代わりに関数ポインタ を関数ポインタ配列に入れておいて、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++言語では使えません。
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 );
標準規格で実装するには、特殊なclassを用意することです。
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 };
標準規格で実装することはできません。