extern "C"をなんとなく付けている人へ

[こちら](https://qiita.com/nomunomu0504/items/722a2771fef7d8038ceb)の記事を読んで気になったので、自分なりにCとC++が混在している場合の注意点や差異について調べました。  
「undefined referenceって言われたけど、なんとなくextern "C"を付けたら上手くいった・・・」という方向け。  
gccコマンドを叩ける前提で書いています。gccコマンドが不安な方は[こちら](https://qiita.com/Cecco/items/c483c415c4bc4b23b32a)。

## 1. Cのみの場合
C言語だけで書かれたソースコードをコンパイルして実行してみます。
```c:func1.c
#include <stdio.h>

void func1()
{
	printf("func1\n");
}
```
```c:main.c
#include <stdio.h>

void func1(void);

int main()
{
	func1();
}
```
```sh
$ gcc -c *.c
$ gcc -o program *.o
$ gcc ./program
func1
```
上手くいきました。ここで、オブジェクトファイルのシンボルをnmコマンドを使って確認してみます。シンボルというのは、プログラム中で関数を特定するための名前だと思ってよいです。
```sh
$ nm func1.o
0000000000000000 T func1
                 U puts
$ nm main.o
                 U func1
0000000000000000 T main
```
ここで大事なのは、`func1()`関数のシンボルが`func1`で統一されていることです。func1.oも、main.oも、同じ`func1`を使っているので、不整合が起きないのです。

## 2. C++のみの場合
同内容で、C++だけで書かれたソースコードをコンパイルして実行してみます。
```cpp:func1.cpp
#include <stdio.h>

void func1()
{
	printf("func1\n");
}
```
```cpp:main.cpp
#include <stdio.h>

void func1();

int main()
{
	func1();
}
```
```sh
$ gcc -c *.cpp
$ gcc -o program *.o
$ gcc ./program
func1
```
上手くいきました。ここで、オブジェクトファイルのシンボルをnmコマンドを使って確認してみます。
```sh
$ nm func1.o
0000000000000000 T _Z5func1v
                 U puts
$ nm main.o
                 U _Z5func1v
0000000000000000 T main
```
先ほどとは違い、シンボルが`_Z5func1v`になっていますが、統一はされていますので、不整合は起きません。ここでは、C++の場合はシンボルに関数名だけでなく色々な情報が付加される、ということがわかればよいです（マングリングと言います）。

## 3. CとC++が混在する場合
では、Cで関数定義をして、C++から呼び出す場合はどうなるでしょうか。  
`func1.c`と`main.cpp`をコンパイルして実行してみましょう。
```sh
$ gcc -c func1.c
$ gcc -c main.cpp
$ gcc -o program *.o
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x9): undefined reference to `func1()'
collect2: error: ld returned 1 exit status
```
ダメでした。シンボルを確認してみましょう。
```sh
$ nm func1.o
0000000000000000 T func1
                 U puts
$ nm main.o
                 U _Z5func1v
0000000000000000 T main
```
なぜダメかというと、C++ソースだった`main.o`側は`_Z5func1v`を探しているのに、Cソースだった`func1.o`側は`func1`を提供しているからです。シンボルが一致していないので、関数が見つからなくなっているというわけです（リンケージが異なる）。  
ではどうすればいいかというと、C++ソース上で、「func1はCリンケージですよ」と教えてあげればよいです。
```cpp:main.cpp
#include <stdio.h>

//void func1(); // これはNG.
extern "C" void func1(); // Cで書かれた関数だと教えてあげる

int main()
{
	func1();
}
```
```sh
$ gcc -c func1.c
$ gcc -c main.cpp
$ gcc -o program *.o
$ ./program
func1
```
上手くいきました。シンボルも確認してみましょう。
```sh
$ nm func1.o
0000000000000000 T func1
                 U puts
$ nm main.o
                 U func1
0000000000000000 T main
```
`main.o`がシンボル`func1`を探すようになったので、無事にシンボルが一致してうまくいくようになりました。

## 4. C++にマングリングがある意味
Cでは、シンボル名が単なる関数名ですが、C++では関数名だけでなく色々と情報が付加されています。少々複雑ですが、C++ではこれにより関数のオーバーロード（関数名は同じでも引数が異なる）が可能になっています。
```cpp:func1.cpp
#include <stdio.h>

void func1()
{
	printf("func1\n");
}

void func1(int num)
{
	printf("func1:%d\n", num);
}
```
```cpp:main.cpp
#include <stdio.h>

void func1();
void func1(int num);

int main()
{
	func1();
	func1(10);
}
```
```sh
$ gcc -c func1.cpp
$ gcc -c main.cpp
$ gcc -o program *.o
$ ./program
func1
func1:10
```
シンボルを見てみましょう。
```sh
$ nm func1.o
000000000000001a T _Z5func1i
0000000000000000 T _Z5func1v
                 U printf
                 U puts
$ nm main.o
                 U _Z5func1i
                 U _Z5func1v
0000000000000000 T main
```
同じ`func1`という関数名でも、シンボルは`_Z5func1i`と`_Z5func1v`で異なっているため、正しくコンパイルできているのです。  

また、C++では名前空間を分けることで、関数名も引数も全く同じ関数を使用することができます（名前空間を指定して呼び出す必要があります）。  
以下は、C++側に同じ関数名の関数を2つ定義し、かつCリンケージにも同じ関数名の関数がある例です。乱暴なことをしているようですがシンボルを確認すると、別のシンボルがそれぞれ与えられており、問題ないことがわかります。
```cpp:func_name1.cpp
#include <stdio.h>

namespace name1
{
	void func()
	{
		printf("func name1\n");
	}
}
```
```cpp:func_name2.cpp
#include <stdio.h>

namespace name2
{
	void func()
	{
		printf("func name2\n");
	}
}
```
```c:func.c
#include <stdio.h>

void func()
{
	printf("func C\n");
}
```
```cpp:main.cpp
#include <stdio.h>

namespace name1 {
	void func();
}
namespace name2 {
	void func();
}
extern "C" void func();

int main()
{
	name1::func();
	name2::func();
	func();
}
```
```sh
$ gcc -c func_name1.cpp
$ gcc -c func_name2.cpp
$ gcc -c func.c
$ gcc -c main.cpp
$ gcc -o program *.o
$ ./program
func name1
func name2
func C
$ nm *.o

func.o:
0000000000000000 T func
                 U puts

func_name1.o:
0000000000000000 T _ZN5name14funcEv
                 U puts

func_name2.o:
0000000000000000 T _ZN5name24funcEv
                 U puts

main.o:
                 U _ZN5name14funcEv
                 U _ZN5name24funcEv
                 U func
0000000000000000 T main
```