如果有人问你,extern "C"的作用是什么?你会如何回答?
先说一段故事:C++创始人在编写C++的时候,C语言正盛行,他不得不让C++兼容C。C++最大的特性就是封装,继承,多态,重载。而这些特性恰恰是C语言所不具备的。至于多态,核心技术是通过虚函数表实现的,其实也就是指针。而对于重载,与C语言相比,其实就是编译方式不同而已: C++编译方式和C编译方式。对于函数调用,编译器只要知道函数的参数类型和返回值以及函数名就可以进行编译连接。extern “C”是让程序按照C的方式编译。我们先来看看C++和C两种编译方式对于究竟有何不同。
main.c

#include <stdio.h>

int func(int a)
{
    printf("a = %d",a);
    return a;
}

int main()
{
    int i=10;
    func(i);

    return 0;
}

gcc -c main.c之后生成main.o我们使用objdump -d main.o查看如下内容:

main.o.c     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <func>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   8b 45 fc                mov    -0x4(%rbp),%eax
   e:   89 c6                   mov    %eax,%esi
  10:   bf 00 00 00 00          mov    $0x0,%edi
  15:   b8 00 00 00 00          mov    $0x0,%eax
  1a:   e8 00 00 00 00          callq  1f <func+0x1f>
  1f:   8b 45 fc                mov    -0x4(%rbp),%eax
  22:   c9                      leaveq 
  23:   c3                      retq   

0000000000000024 <main>:
  24:   55                      push   %rbp
  25:   48 89 e5                mov    %rsp,%rbp
  28:   48 83 ec 10             sub    $0x10,%rsp
  2c:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
  33:   8b 45 fc                mov    -0x4(%rbp),%eax
  36:   89 c7                   mov    %eax,%edi
  38:   e8 00 00 00 00          callq  3d <main+0x19>
  3d:   b8 00 00 00 00          mov    $0x0,%eax
  42:   c9                      leaveq 
  43:   c3                      retq   

我们可以看到函数func就是func,在.o文件中名字没有变化。
main.cpp

#include <iostream>

int func(int a)
{
    return a;
}

int main()
{
    int i=10;
    func(i);

    return 0;
}

使用g++ -c main.cpp之后生成main.o,同样使用objdump -d main.o看到如下内容:

main.o     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <_Z4funci>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   5d                      pop    %rbp
   b:   c3                      retq   

000000000000000c <main>:
   c:   55                      push   %rbp
   d:   48 89 e5                mov    %rsp,%rbp
  10:   48 83 ec 10             sub    $0x10,%rsp
  14:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
  1b:   8b 45 fc                mov    -0x4(%rbp),%eax
  1e:   89 c7                   mov    %eax,%edi
  20:   e8 00 00 00 00          callq  25 <main+0x19>
  25:   b8 00 00 00 00          mov    $0x0,%eax
  2a:   c9                      leaveq 
  2b:   c3                      retq   

000000000000002c <_Z41__static_initialization_and_destruction_0ii>:
  2c:   55                      push   %rbp
  2d:   48 89 e5                mov    %rsp,%rbp
  30:   48 83 ec 10             sub    $0x10,%rsp
  34:   89 7d fc                mov    %edi,-0x4(%rbp)
  37:   89 75 f8                mov    %esi,-0x8(%rbp)
  3a:   83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
  3e:   75 27                   jne    67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
  40:   81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
  47:   75 1e                   jne    67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
  49:   bf 00 00 00 00          mov    $0x0,%edi
  4e:   e8 00 00 00 00          callq  53 <_Z41__static_initialization_and_destruction_0ii+0x27>
  53:   ba 00 00 00 00          mov    $0x0,%edx
  58:   be 00 00 00 00          mov    $0x0,%esi
  5d:   bf 00 00 00 00          mov    $0x0,%edi
  62:   e8 00 00 00 00          callq  67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
  67:   c9                      leaveq 
  68:   c3                      retq   

0000000000000069 <_GLOBAL__sub_I__Z4funci>:
  69:   55                      push   %rbp
  6a:   48 89 e5                mov    %rsp,%rbp
  6d:   be ff ff 00 00          mov    $0xffff,%esi
  72:   bf 01 00 00 00          mov    $0x1,%edi
  77:   e8 b0 ff ff ff          callq  2c <_Z41__static_initialization_and_destruction_0ii>
  7c:   5d                      pop    %rbp
  7d:   c3                      retq   

我们可以看到函数func名字变为了_Z4funci,因此在c/c++混合编程中经常看到如下方式:

#ifndef __INCvxWorksh  /*防止该头文件被重复引用*/
#define __INCvxWorksh

#ifdef __cplusplus    //__cplusplus是cpp中自定义的一个宏
extern "C" {          //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
#endif

    /**** some declaration or so *****/  

#ifdef __cplusplus
}
#endif

#endif /* __INCvxWorksh */

例子一:
animal.h

#ifndef __ANIMAL_H__  //防止被重复包含
#define __ANIMAL_H__

class ANIMAL{
public:
        ANIMAL(char* );
        ~ANIMAL();
        char* getname(void) const;
private:
        char* name;
};

#ifdef __cplusplus
extern "C" {
#endif
void print(void);
#ifdef __cplusplus
}
#endif
#endif  // __ANIMAL_H__

animal.cpp

#include "animal.h"
#include <iostream>
#include <string.h>
using namespace std;

ANIMAL::ANIMAL(char *data)//构造函数
{       name = new char[64];
        strcpy(name, data);
}

ANIMAL::~ANIMAL() //析构函数

{
        if(name)
        {

                delete[] name;
                name = NULL;
        }
}

char* ANIMAL::getname(void) const
{        return name;
}

void print(void) //对外接口,而且必须有一个非类中方法,才能被C调用
{
        char name[]="dog";
        ANIMAL animal(name);
        char* animal_name = animal.getname();
        cout << "animal name is :" << animal_name << endl;
}

main.c

#include<stdio.h>

int main(void)

{       
        print();
        return 0;
}

使用如下命令编译:
g++ -c animal.cpp
gcc -lstdc++ main.c animal.o -o main

执行./main输出内容如下:
animal name is :dog

如果在编译中未加-lstdc++的话会有如下错误:

[root@centos-linux-7 func]# gcc main.c animal.o -o main
animal.o:在函数‘ANIMAL::ANIMAL(char*)’中:
animal.cpp:(.text+0x16):对‘operator new[](unsigned long)’未定义的引用
animal.o:在函数‘ANIMAL::~ANIMAL()’中:
animal.cpp:(.text+0x69):对‘operator delete[](void*)’未定义的引用
animal.o:在函数‘print’中:
animal.cpp:(.text+0xc4):对‘std::cout’未定义的引用
animal.cpp:(.text+0xc9):对‘std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)’未定义的引用
animal.cpp:(.text+0xd8):对‘std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)’未定义的引用
animal.cpp:(.text+0xdd):对‘std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)’未定义的引用
animal.cpp:(.text+0xe5):对‘std::ostream::operator<<(std::ostream& (*)(std::ostream&))’未定义的引用
animal.o:在函数‘__static_initialization_and_destruction_0(int, int)’中:
animal.cpp:(.text+0x13b):对‘std::ios_base::Init::Init()’未定义的引用
animal.cpp:(.text+0x14a):对‘std::ios_base::Init::~Init()’未定义的引用
animal.o:(.eh_frame+0x8b):对‘__gxx_personality_v0’未定义的引用
collect2: 错误:ld 返回 1

对应的Makefile:

main:main.c animal.o
    gcc -lstdc++ main.c animal.o -o main

animal.o:animal.h animal.cpp
    g++ -c animal.cpp
.PHONY : clean
clean:
    -rm animal.o main

Makefile基本知识可以参考我的笔记Makefile与C

C++调用C函数

C++调用C函数的例子: 引用C的头文件时,需要加extern "C"

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

//cExample.c
#include "cExample.h"
int add( int x, int y ) {
  return x + y;
}

//cppExample.cpp
extern "C" {
  #include "cExample.h"
}

int main(int argc, char* argv[]) {
  add(2,3); 
  return 0;
}

Makefile
CFLAGS=-O2 -Wall
main: cExample.o cppExample.o
    g++ $(CFLAGS) -o main cExample.o cppExample.o

cExample.o: cExample.c
    gcc $(CFLAGS) -c cExample.c

cppExample.o: cppExample.cpp 
    g++ $(CFLAGS) -c cppExample.cpp

clean:
    rm -f *.o main

注意,如果cppExample.cpp中没有extern "c"的话,会在最后一步链接的时候出错。

C调用C++函数

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

// cppExample.cpp
#include "cppExample.h"
int add( int x, int y ) {
  return x + y;
}

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

Makefile
CFLAGS=-O2 -Wall
main: cExample.o cppExample.o
    g++ $(CFLAGS) -o main cExample.o cppExample.o

cppExample.o: cppExample.cpp 
    g++ $(CFLAGS) -c cppExample.cpp
cExample.o: cExample.c
    gcc $(CFLAGS) -c cExample.c
clean:
    rm -f *.o main