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)