[ page1 ] [ page2 ] [ page3 ] [ 標準ライブラリ ] [ 組み込み関数 ] [ struct ] [ Pythonista ] [ UIエディタ ] [ ui ] [ venv ] [ apt ] [ bash ] [ makefile ] [ vim ] [ python ] [ sc-im ] [ gdb ] [ tmux ] [ tmux のコピー ] [ bgへ入力 ] [ C/C++ ] [ C/C++ 1 ] [ MSVC ] [ bash ] [ vim ] [ git ]

CとC++のプログラムをリンクするときの注意点

本 Hack では、CからC++の関数を呼び出す方法、逆にC++からCの関数を呼び出す方法を紹介します。

C++からCで書かれた関数を呼び出したくなることはよくあります。あるいは逆に、CからC++で書かれた関数を呼び出したくなることもたまにはあるでしょう。本Hackでは、それらを実現する方法と、その際の注意点を解説します。基礎的なHackですが、一から順に確認していきましょう。

C/C++ とシンボル名

次の dbg 関数を、C コンパイラ、C++ コンパイラの両方でコンパイルしてみます。

//
// dbg.c
//
#include <stdio.h>
void dbg(const char *s) {
  printf("Log: %s\n", s);
}

すると、生成されたオブジェクトに含まれるシンボル名は(例えば)次のようになるでしょう。

コンパイラ シンボル名
C dbg
C++ _Z3dbgPKc

Cコンパイラで関数をコンパイルすると、基本的には関数名がそのままシンボル名になります。環境によってはシンボル名が "dbg"  ではなく、"_dbg" になる場合もありますが、その程度の変化です。一方、C++コンパイラで関数をコンパイルした場合、[Hack #16] で解説しているように、関数の所属する名前空間の情報や、関数の引数の型情報がシンボルに含まれるようになります。

C++ から C の関数を呼び出す

さて、dbg.c を C コンパイラでコンパイルし、それを C++ で書かれた関数から呼び出して見ましょう。次の sample.cpp を用意します。

//
// sample.cpp
//
extern "C" void dbg(const char *s):
int main() {
  dbg("foo");
  return 0;
}

これを、次のようにコンパイル、リンクすると、正常に実行ファイルが生成されます。無事、C++の関数からCの関数を呼び出すことができました。

% gcc -Wall -c dbg.c
% g++ -Wall -c sample.cpp
% g++ -o sample dbg.o sample.o
% ./sample
Log: foo

sample.cpp の4行目、extern "C" がポイントです。extern "C" の有無で sampole.o の内容がどう変化するか見てみましょう (nmコマンドの使い方は、「[Hack #12] nmでオブジェクトファイルに含まれるシンボルをチェックする」を参照してください)。

(1) extern "C" あり

% nm sample.o
         U __gxx_personality_v0
00000000 T main
         U dbg

(2) extern "C" なし

% nm sample.o
         U __gxx_personality_v0
00000000 T main
         U _Z3dbgPKc

(2) では、sample.o は "_Z3dbgPKc" というシンボルを参照していますが、dbg.o に含まれるシンボルは単なる "dbg" であり、"_Z3dbgPKc"はどこにも存在しません。これにより、extern "C" をつけないと、次のようにリンクに失敗する結果となります。

% g++ -o sample dbg.o sample.o
sample.o(.text+0x25): In function `main':
: undefined reference to `dbg(char const*)'

このように、C++からCの関数を呼び出す際には、extern "C" が重要な役割を果たします。


Yusuke Sato / BINARY HACKS (2006)


[ ホーム ]