GCCの文法拡張のうち、個人的に使ったことのなかったものをまとめました。

文を式として扱う

複文を括弧で括ることによって、その複文を式として扱うことができます。この際、最後の文の実行結果がそのままその式の値となります。RubyやElixirみたいな感じの挙動。

int main() {
     for (int i = 1; i <= 100; i++) {
          char msg[10];
          printf("%s\n", ({
                         if (i % 15 == 0) strcpy(msg, "Fizz Buzz");
                         else if (i % 3 == 0) strcpy(msg, "Fizz");
                         else if (i % 5 == 0) strcpy(msg, "Buzz");
                         else sprintf(msg, "%d", i);
                         msg;
                    }));
     }
}

実行結果:

$ ./a.out
1
2
Fizz
4
Buzz
Fizz
...

ラベルを値として扱う

ラベルをvoid *の値として扱うことができます。ラベルの指すアドレスは&&によって取得することができます。

int main() {
     void *lab = &&LABEL;
     goto *lab;
     return 0;
LABEL:
     printf("heyhey!\n");
     return 0;
}
$ ./a.out
heyhey!

Typeof 演算子

typeof(a)で式aの型を取得することができます。たぶんC++のdecltypeに近いもののはず。C++知らないけど。

#define MAX(a, b)                               \
     ({                                         \
          typeof(a) _a = (a);                   \
          typeof(b) _b = (b);                   \
          _a > _b ? _a : _b;                    \
     })
int main() {
     printf("MAX(5, 3): %d\n", MAX(5, 3));
     return 0;
}

実行結果:

$ ./a.out
5

__auto_type

typeofよりもう一段階便利な機能。__auto_typeを使うことで、処理系が型推論をしてくれるようになります。

#define MAX(a, b)                                   \
     ({                                             \
          __auto_type _a = (a);                     \
          __auto_type _b = (b);                     \
          _a > _b ? _a : _b;                        \
     })
int main() {
     printf("MAX(5, 3): %d\n", MAX(5, 3));
     return 0;
}

実行結果:

$ ./a.out
5

三項演算子の二項目の省略

x ? : yx ? x : yに近い挙動をするようになります。ただし、xは一度しか評価されません。

int main() {
     char *ptr = NULL;
     ptr = ptr ? : "hello, world!";
     printf("%s\n", ptr);
     return 0;
}

実行結果:

$ ./a.out
hello, world!

可変長引数マクロ

マクロも可変長引数を受け取ることができます。

#define debugprint(fmt, args...)                                        \
     printf("%s %d: %s: " fmt, __FILE__, __LINE__, __FUNCTION__, args)
int main() {
     debugprint("%s\n", "hello!");
     return 0;
}

実行結果:

$ ./a.out
hoge.c 4: main: hello!

caseで範囲にマッチ

RubyのRangeのような表記が使えます。

int main() {
     int height = 156;
     switch (height) {
     case 150 ... 160:
          puts("you are extremely short!");
          break;
     case 161 ... 169:
          puts("you are short!");
          break;
     case 170 ... 175:
          puts("you are normal.");
          break;
     case 176 ... 179:
          puts("you are tall!");
          break;
     case 180 ... 190:
          puts("you are extremely tall!");
          break;
     }
     return 0;
}

実行結果:

$ ./a.out
you are extremely short!

配列の特定のインデックスのみ初期化

配列の初期化子に、[n]のような形式でインデックスを指定することができます。

int main() {
     int arr[10] = {
          [0] = 0,
          [1 ... 8] = 1,
          [9] = 2
     };
     for (int i = 0; i < 10; i++) {
          printf("%d ", arr[i]);
     }
     printf("\n");
     return 0;
}

実行結果:

$ ./a.out
0 1 1 1 1 1 1 1 1 2

ドル記号

$が識別子に使えます。

int main() {
     char *$ = "JQuery";
     printf("%s must die!\n", $);
     return 0;
}

実行結果:

$ ./a.out
JQuery must die!

その他

完全な拡張文法のリストは公式のドキュメントを参照してください。