gcc拡張を使って、main関数より前にFizzBuzzを実行させてみた
gcc拡張を使うとmain関数の前に色々出来ると聞いたので、遊んでみた。
結論からいうと、二通りのやりかたがありそうで、私の環境*1ではどちらも問題無く動作した。
__attribute__((constructor)) という指定
関数の宣言に際して、__attribute__で関数にconstructor属性を付け加える。constructor属性がある関数はmain関数よりも先で呼び出されるんだってさ。
ほんとかねえ?
実験してみる。
main関数が呼び出されたらエラーを出力するので、すぐ分かります。
#include <stdio.h> #include <stdlib.h> #ifdef __GNUC__ int fizzbuzz() __attribute__((constructor)); #endif int main(void){ perror("NO GOOD\n"); } int fizzbuzz(){ int i; for(i=1; i<=100; i++){ if(!(i%15)){ printf("FizzBuzz "); }else if(!(i%5)){ printf("Buzz "); }else if(!(i%3)){ printf("Fizz "); }else{ printf("%d ", i); } } exit(0); }
普通にFizzとかBuzzとか言ってくれたら成功です。(あ、そういえばfizzbuzz関数の返り値がない)
あ、ちなみに __GNUC__ は、gcc環境なら必ず定義されてるそうです。__attribute__が使えるかどうかのチェックのため、みたいなつもりです。
__attribute__((section(".init"))) という指定
ELF形式ではmain関数が呼び出される前に処理されるセクションがあって、その名前が .init 。__attribute__には関数を置くセクションを指定することもできるので、その機能を利用してみた。
もしかしたら、先ほど挙げた方法はこれのシンタックス糖だったのかもしれない。
#include <stdio.h> #include <stdlib.h> #ifdef __GNUC__ int fizzbuzz() __attribute__((section (".init"))); #endif int main(void){ perror("NO GOOD\n"); } int fizzbuzz(){ int i; for(i=1; i<=100; i++){ if(!(i%15)){ printf("FizzBuzz "); }else if(!(i%5)){ printf("Buzz "); }else if(!(i%3)){ printf("Fizz "); }else{ printf("%d ", i); } } exit(0); }
まったく同一に動作した。
gccでコンパイルできたけどVC++でコンパイルできなかったことがあったけど、それはきっと、もっと微妙なgcc拡張を知らず知らずのうちに使ってしまっていたことも理由の一部なんだろうね、と実感した。
[参考記事]
http://www.asahi-net.or.jp/~wg5k-ickw/html/online/gcc-2.95.2/gcc_4.html
http://www.globe.to/~oka326/archive/elf_doc_sgml_ja/elf_doc-3.html