●C++編(言語解説) 第38章 C言語との連携

○C言語との連携

1つのアプリケーションを開発するとき、そこに含まれる全てのソースファイルがC++で書かれていれば何も 問題ありません。しかし、場合によっては一部のソースがC言語で書かれており、それをC++のソースから使用 しなければならない(=つまりC言語で書いた関数を呼び出す等)ことがあります。

そういった状況はあまり想像できないと思うかも知れませんが、古い開発プロジェクトで作成し、非常に安定 して動作しているものがあれば(そしてそれを再度C++で書き直す労力が惜しい場合)、それを再利用できること は望ましいことでしょう。逆に、後にC++で使うことを考慮して、最初からあえてC言語だけでプログラムを書く こともあります。

では、C++からC言語の関数を呼び出すにはどうすればいいのでしょう。C++は、C言語の機能が(基本的には) すべて使えるということでした。実際、C++で次のように関数を定義することができますね。

int function(int num)
{
	return num * 2;
}

これだけ見ると、C言語なのかC++なのか分かりません。つまり違いがありません(ただし、C++ではプロトタイプ 宣言を書くことが必須になります。定義部分には違いがありません)。それならば、次のようにできるでしょうか?

// main.cpp
#include <iostream>
#include "test.h"

int main()
{
	std::cout << function(100) << std::endl;
	return 0;
}
/* test.c */

int function(int num)
{
	return num * 2;
}
/* test.h */

int function(int num);

これをコンパイルすると、VisualC++2005なら、このようなエラーが出ます。

main.obj : error LNK2001: 外部シンボル ""int __cdecl function(int)" (?function@@YAHH@Z)" は未解決です。

ちなみにこれはコンパイルエラーではなく、リンクエラーです。コンパイル段階は成功していますが、コンパイルに よって作られたオブジェクトファイルのリンク(結合)に失敗しています。「○○○は未解決です」と言っていますが、 要するに、そんなものは見つからない、と言っている訳です。

main.cppは、test.hをincludeしており、test.hには確かにfunction()のプロトタイプ宣言があります。そして、test.c にはfunction()の定義が書かれています。一見して問題はなさそうです。

リンクが失敗してしまう理由は、C++とC言語とでは、コンパイル後の関数名に変化が生じるからです。C言語の場合、 「function」という名前の関数を定義したら、同じものが現れることはありません。しかしC++の場合、同じ名前の関数 をオーバーロードできる機能があります。そのため、オーバーロードされたそれぞれの関数を識別するため、勝手に関数名 に文字列を付加します。それが先ほどのエラーメッセージにある「@@YAHH@Z」という謎の文字列です。

○extern "C"指定子

ではどうすればいいのか、というと、extern指定子を利用します。具体的には、先ほどの3つのソースのうちの「test.h」 を次のように書き換えます。

/* test.h */

extern "C" int function(int num);

「extern "C"」を、関数宣言の先頭に加えることにより、その関数がC言語形式 になっていることを示します。すると、この関数をC++から呼び出す場合、C言語の関数名のまま使おうとします。これで、 C++から呼び出せるようになりました。

ちなみに、更に関数が増えて「function2()」「function3()」・・・となっていた場合、次のようにまとめてextern "C" を適用できます。

/* test.h */

extern "C" {
int function(int num);
int function2(int num);
int function3(int num);
}

さて、extern "C"を加えることで、C++からC言語の関数は呼び出せるようになりました。しかし、extern "C"という キーワードはC++のものです。従って、C言語からC言語を呼び出すときには「extern "C"」って何? と言われてしまい ます。そこで、更に次のように一工夫を加える必要があります。

/* test.h */

#ifdef __cplusplus
extern "C" {
#endif

int function(int num);
int function2(int num);
int function3(int num);

#ifdef __cplusplus
}
#endif

__cplusplusマクロは、C++で開発しているときにだけ定義されます。C言語の場合、 このマクロが定義されていないため、extern "C"の部分は消えてなくなります。{ } での表記法は、単に記述を簡単に するためというだけでなく、このような用途に対応しやすくするためにも存在しているといえます。

ただし__cplusplusマクロは、C++の標準仕様ではないので、一部のコンパイラには存在しない可能性があります。 その場合は、同じ目的のものを独自に#defineで定義すればいいでしょう。


C++編(言語解説)のトップページに戻る

サイトのトップページに戻る