コンテンツにスキップ

最終更新:2026-10-12

C++ extern リンケージ指定

著者について:

個人公式アカウント:

wechat

1.C++とCのコンパイルの違い

C++ではヘッダファイルでextern "C"と修飾された関数をよく見かけますが、何の役割があるのでしょうか? C言語モジュールで定義された関数をC++からリンクするためのものです。

C++はCと互換性がありますが、C++ファイルの関数をコンパイルして生成されるシンボルは、C言語が生成するものと異なります。C++は関数のオーバーロードをサポートしており、C++の関数をコンパイルして生成されるシンボルには関数の引数型の情報が含まれますが、Cには含まれません。

例えばint add(int a, int b)関数をC++コンパイラで.oファイルに生成すると、addはadd_int_intのような形になり、Cなら_addのようになります。つまり、同じ関数でもCとC++では、コンパイル後に生成されるシンボルが異なるのです。

これがある問題を引き起こします:C++の中でC言語で実装された関数を使うと、コンパイル・リンクのときにエラーになり、対応するシンボルが見つからないと示されます。ここでextern "C"が役に立ちます:リンカに、C++で修飾されたシンボルではなく、_addのようなC言語のシンボルを探すよう伝えるのです。

2.C++からC関数を呼ぶ

C++がC関数を呼ぶ例:Cのヘッダファイルを読み込むときは、extern "C"を追加する必要があります

//add.h
#ifndef ADD_H
#define ADD_H
int add(int x,int y);
#endif

//add.c
#include "add.h"

int add(int x,int y) {
    return x+y;
}

//add.cpp
#include <iostream>
#include "add.h"
using namespace std;
int main() {
    add(2,3);
    return 0;
}

コンパイル:

//Generate add.o file
gcc -c add.c

リンク:

g++ add.cpp add.o -o main

extern "C"を追加していない場合のエラー:

> g++ add.cpp add.o -o main                                   
add.o:在函数‘main’中:
add.cpp:(.text+0x0): `main'被多次定义
/tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义
/tmp/ccH65yQF.o:在函数‘main’中:
add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用
add.o:在函数‘main’中:
add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用
collect2: error: ld returned 1 exit status

extern "C"を追加した後:

add.cpp

#include <iostream>
using namespace std;
extern "C" {
    #include "add.h"
}
int main() {
    std::cout << add(2, 3) << std::endl;
    return 0;
}

コンパイルするときは、必ず先にgccで中間ファイルadd.oを生成します。

gcc -c add.c 

そしてコンパイル:

g++ add.cpp add.o -o main

bazelでコンパイルする場合は、実行します:bazel run basic_content/extern/extern_c++:main

また、通常Cコードを汎用的に、すなわちCからもC++からも呼べるようにするため、ヘッダファイルは通常次のように書かれます:

#ifdef __cplusplus
extern "C"{
#endif
int add(int x,int y);
#ifdef __cplusplus
}
#endif

すなわち、C++がこのインターフェースを呼ぶとき、Cインターフェースの方式で呼ばれます。この方式により、C++側は追加のextern Cが不要になります。標準ライブラリのヘッダファイルも通常は同様のやり方です。そうでなければ、なぜあなたはextern Cなしでstdio.hのC関数を直接使えるのでしょうか?

上記の例のソースコードはこちら:

2.CからC++関数を呼ぶ

extern "C"はCの中では文法エラーとなるため、C++のヘッダファイルに置く必要があります。

// add.h
#ifndef ADD_H
#define ADD_H
extern "C" {
    int add(int x,int y);
}
#endif

// add.cpp
#include "add.h"

int add(int x,int y) {
    return x+y;
}

// add.c
extern int add(int x,int y);
int main() {
    add(2,3);
    return 0;
}

コンパイル:

g++ -c add.cpp

リンク:

gcc add.c add.o -o main

上記の例のソースコードはこちら:

以上より、使用方法をまとめます。C言語のヘッダファイルの中では、その外部関数に指定できるのはextern型だけであり、C言語はextern "C"宣言をサポートしません。.cファイルにextern "C"が含まれていると、コンパイルの文法エラーになります。したがってextern "C"の使用は、すべてcppプログラムの関連ファイルまたはそのヘッダファイルに置きます。

次のような形式がまとめられます:

(1)C++がC関数を呼ぶ:

//xx.h
extern int add(...)

//xx.c
int add(){

}

//xx.cpp
extern "C" {
    #include "xx.h"
}

(2)CがC++関数を呼ぶ

//xx.h
extern "C"{
    int add();
}
//xx.cpp
int add(){

}
//xx.c
extern int add();

ただし、C++がCインターフェースを呼ぶ場合と異なり、C++はコンパイル済みのC関数を本当に呼び出せますが、ここでのCからC++を呼ぶのは、C++のコードをCコードとしてコンパイルしてから呼んでいるにすぎません。つまり、CはC++のライブラリ関数を直接呼び出すことはできないのです。

関連トピック

コメント