extern 関数とプロトタイプ宣言
マルチポスト元→http://exth.net/~tgbt/wordpress/2009/04/24/1972/
罠にはまったのでメモっておく.ちなみにgccのバージョンは4.1.2.
C++プログラムからCの関数を呼ぶにはextern "C".これ常識.
func.h
#ifdef __cplusplus extern "C"{ #endif int func(int); #ifdef __cplusplus } #endif
func.c
#include "func.h" int func(int n) { return n + 1; }
main.c / main.cpp
#include <stdio.h> #include "func.h" int main() { int n = func(0); printf("n = %d\n", n); return 0; }
$ gcc -o -c func.o func.c $ gcc -o main_c func.o main.c $ g++ -o main_cpp func.o main.cpp $ ./main_c n = 1 $ ./main_cpp n = 1 $
ところで,Cの関数プロトタイプ宣言って,引数の宣言を省略してもOKなんだよね.
func.h 修正版
#ifdef __cplusplus extern "C"{ #endif //int func(int); int func(); #ifdef __cplusplus } #endif
これでもCとしてコンパイル,リンク,実行が可能.
$ gcc -c -o func.o func.c $ gcc -o main_c func.o main.c $ ./main_c n = 1
しかしC++としてコンパイルしようとするとがっかりなことに.
$ g++ -c -o func.o func.c $ g++ -o main_cpp func.o main.cpp func.h: In function ‘int main()’: func.h:6: error: too many arguments to function ‘int func()’ main.cpp:6: error: ファイルのこの位置
Cだと関数名が被ることはないから問題ないけど,C++だとオーバーロードの都合で型が違うと問題が起きるってことかな.プログラムを機械的に変換してたらはまってしばらく悩んだ.
ちなみにCの場合,省略は可能だけど違う型は駄目.以下NG集.
#ifdef __cplusplus extern "C"{ #endif void func(); #ifdef __cplusplus } #endif
#ifdef __cplusplus extern "C"{ #endif int func(double); #ifdef __cplusplus } #endif
さて,さらに引数の省略ができちゃったんだけど,これってどういう仕様だっけ?記憶にないんだよなあ.
func2.c
#include <stdio.h> int hoge(int n, void *v) { printf("hoge n = %d\n", n); printf("hoge v = %d\n", v); if(v!=NULL) { int *tmp = (int*)v; printf("hoge v = %d\n", *tmp); } return 0; }
main2.c
#include <stdio.h> //extern int hoge(int n, void *v); // これはNG //extern int hoge(int n, void *v=NULL); // これもNG extern int hoge(); int main() { int key = 0; int value = 99; printf("value's address = %x(%d)\n", (void*)&value, ((int*)((void*)&value))); key = 1; hoge(key, (void*)&value); key = 2; hoge(key); return 0; }
$ gcc -Wall -c -o func2.o func2.c func2.c: In function ‘hoge’: func2.c:6: 警告: format ‘%d’ expects type ‘int’, but argument 2 has type ‘void *’ $ gcc -Wall -c -o main2.o main2.c main2.c: In function ‘main’: main2.c:12: 警告: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘void *’ main2.c:12: 警告: format ‘%d’ expects type ‘int’, but argument 3 has type ‘int *’ $ gcc -Wall -o main2 main2.o func2.o $ ./main2 value's address = eba86198(-341286504) hoge n = 1 hoge v = -341286504 hoge v = 99 hoge n = 2 hoge v = -1090371584 // なんのアドレスだろうこれ?実行する罰に違うっぽい hoge v = 1701277544 // よくわからない値,毎回同じっぽい
んー,C++じゃないのに引数の省略ができちゃってるな.なんでだろう?C++みたいに引数の初期値(なんて言うんだっけ?)の指定はできないみたいだけど,
まぁ今回はCプログラムをC++に移植しようとしている側なので,externするなりオーバーロードするなりしてなんとかするか.
ちなみにC++でやるとこうなる.こっちは納得.
main2.cpp
#include <stdio.h> extern int hoge(int n, void *v=NULL); //extern int hoge(); // むしろこちらがエラーする int main() { int key = 0; int value = 99; printf("value's address = %x(%d)\n", (void*)&value, ((int*)((void*)&value))); key = 1; hoge(key, (void*)&value); key = 2; hoge(key); return 0; }
$ g++ -Wall -c -o func2_cpp.o func2.cpp func2.cpp: In function ‘int hoge(int, void*)’: func2.cpp:6: 警告: format ‘%d’ expects type ‘int’, but argument 2 has type ‘void*’ $ g++ -Wall -c -o main2_cpp.o main2.cpp main2.cpp: In function ‘int main()’: main2.cpp:12: 警告: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘void*’ main2.cpp:12: 警告: format ‘%d’ expects type ‘int’, but argument 3 has type ‘int*’ $ g++ -Wall -o main2_cpp main2_cpp.o func2_cpp.o $ ./main2_cpp value's address = 2816f878(672594040) hoge n = 1 hoge v = 672594040 hoge v = 99 hoge n = 2 hoge v = 0
追記:func.cの中身がmain.cになっていたのを修正(2009-04-28 1:00)