extern "C" 详解

晨曦之光 发布于 2012/03/09 14:17
阅读 185
收藏 0

本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!

 

extern "C" 详解

 

  • C++中,为了支持重载机制,在编译生成汇编代码时,函数的名字要加入函数的参数类型或者返回值类型等信息
  • C中,因没有重载机制,编译后的代码只是简单的函数名字而已,不加入其他的信息 

1. 不加入extern "C"

 

testexternc.cpp

int mytest(void)

{

int a=10,b=20;

int c=a+b;

return c;

}

 

Command: g++ -S testexternc.cpp或者gcc -S testexternc.cpp

Generated file: testexternc.s

        .file        "testexternc.cpp"

        .text

        .align 2

.globl _Z6mytestv

        .type        _Z6mytestv, @function

_Z6mytestv:

.LFB2:

        pushl        %ebp

.LCFI0:

        movl        %esp, %ebp

.LCFI1:

        subl        $16, %esp

.LCFI2:

        movl        $10, -12(%ebp)

        movl        $20, -8(%ebp)

        movl        -8(%ebp), %eax

        addl        -12(%ebp), %eax

        movl        %eax, -4(%ebp)

        movl        -4(%ebp), %eax

        leave

        ret

.LFE2:

        .size        _Z6mytestv, .-_Z6mytestv

.globl __gxx_personality_v0

        .ident        "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"

        .section        .note.GNU-stack,"",@progbits

 

2. 加入extern "C"

 

testexternc.cpp

#ifdef __cplusplus

extern "C"

{

#endif

 

int mytest(void)

{

int a=10,b=20;

int c=a+b;

return c;

}

 

#ifdef __cplusplus

}

#endif

extern "C" int mytest(void)

{

int a=10,b=20;

int c=a+b;

return c;

}

extern "C"

{

int mytest(void)

{

int a=10,b=20;

int c=a+b;

return c;

}

}

三者是等价的。

 

__cpluspluscpp中的自定义宏,表示这是一段c++代码。以上黄色部分的内容表示,如果定义了__cplusplus,则用extern "C"{}处理其中的代码。

 

Command: g++ -S testexternc.cpp

Generated file: testexternc.s

        .file        "testexternc.cpp"

        .text

        .align 2

.globl mytest

        .type        mytest, @function

mytest:

.LFB2:

        pushl        %ebp

.LCFI0:

        movl        %esp, %ebp

.LCFI1:

        subl        $16, %esp

.LCFI2:

        movl        $10, -12(%ebp)

        movl        $20, -8(%ebp)

        movl        -8(%ebp), %eax

        addl        -12(%ebp), %eax

        movl        %eax, -4(%ebp)

        movl        -4(%ebp), %eax

        leave

        ret

.LFE2:

        .size        mytest, .-mytest

.globl __gxx_personality_v0

        .ident        "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"

        .section        .note.GNU-stack,"",@progbits

 

编译生成的代码基本相同,唯独产生的函数名不同。加入extern "C"生成的函数名为mytest,即源代码定义的函数名本身;不加入extern "C"生成的函数名为_Z6mytestv

 

3. 带参数的函数汇编代码

 

还可以看看如下代码不加入extern "C"生成的汇编代码。

 

testexternc.cpp

int mytest(int a,int b)

{

int c=a+b;

return c;

}

 

生成的汇编代码如下:

        .file   "testexternc.cpp"

        .text

        .align 2

.globl _Z6mytestii

        .type   _Z6mytestii, @function

_Z6mytestii:

.LFB2:

        pushl   %ebp

.LCFI0:

        movl    %esp, %ebp

.LCFI1:

        subl    $16, %esp

.LCFI2:

        movl    12(%ebp), %eax

        addl    8(%ebp), %eax

        movl    %eax, -4(%ebp)

        movl    -4(%ebp), %eax

        leave

        ret

.LFE2:

        .size   _Z6mytestii, .-_Z6mytestii

.globl __gxx_personality_v0

        .ident  "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"

        .section        .note.GNU-stack,"",@progbits

 

可以看出,生成的函数名在_Z6mytest后面加上了表示函数参数类型inti两个。

 

4. 3的基础上加入extern "C"

 

testexternc.cpp

extern "C" int mytest(int a,int b)

{

int c=a+b;

return c;

}

 

          .file   "testexternc.cpp"

.text

.align 2

.globl mytest

.type        mytest, @function

mytest:

.LFB2:

pushl        %ebp

.LCFI0:

movl        %esp, %ebp

.LCFI1:

subl        $16, %esp

.LCFI2:

movl        12(%ebp), %eax

addl        8(%ebp), %eax

movl        %eax, -4(%ebp)

movl        -4(%ebp), %eax

leave

ret

.LFE2:

.size        mytest, .-mytest

.globl __gxx_personality_v0

.ident        "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"

.section        .note.GNU-stack,"",@progbits

 

此时按照C规则生成函数名。

 

5. 若将3的文件改为.c

 

        .file        "testexternc.c"

        .text

.globl mytest

        .type        mytest, @function

mytest:

        pushl        %ebp

        movl        %esp, %ebp

        subl        $16, %esp

        movl        12(%ebp), %eax

        addl        8(%ebp), %eax

        movl        %eax, -4(%ebp)

        movl        -4(%ebp), %eax

        leave

        ret

        .size        mytest, .-mytest

        .ident        "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"

        .section        .note.GNU-stack,"",@progbits

 

可以看出生成的函数名与3相同,汇编代码的逻辑也一样,不同的是少了C++汇编后的LFB2LCFI0LCFI2LFE2等符号。

 

6. 为什么要使用extern "C"

 

回答这个问题,我们先看一个例子。

 

设计一个实际应用:

我们有一个用C语言写好的两个整数加法的库,包括add.hadd.c两个文件;但现在要在C++的程序(或者.cpp文件)中使用这个加法库。

 

先不考虑extern "C",我们看看这个程序编译和链接过程。

 

add.h文件

int addition(int a,int b);

 

add.c文件

#include "add.h"

 

int addition(int a,int b)

{

int c=a+b;

return c;

}

 

main.cpp文件

#include "stdio.h"

#include "add.h"

extern int addition(int a,int b);

 

int main()

{

int a,b,c;

 

printf("input a:");

scanf("%d",&a);

printf("input b:");

scanf("%d",&b);

 

c=addition(a,b);

printf("the result is %d/n",c);

 

return 0;

}

 

add.cmain.cpp的编译均能通过,但在链接main.oadd.o时出现错误。

 

[root@yu28 testexternc4]# gcc -o add.o -c add.c

[root@yu28 testexternc4]# gcc -o main.o -c main.cpp

[root@yu28 testexternc4]# gcc -lstdc++ -o main main.o add.o

main.o: In function `main':

main.cpp:(.text+0x5d): undefined reference to `addition(int, int)'

collect2: ld returned 1 exit status

[root@yu28 testexternc4]#

使用gcc链接C++程序时需要加上-lstdc++选项。gcc根据源文件的后缀来确定代码所使用的语言以及默认要链接的库。

 

在编译main.cpp时编译器是按照C++的方式来处理addition函数的,但实际上链接的库文件却是用C的方式来处理该函数的,因此编译器找不到addition函数,出现"undefined reference to 'addition(intuit)'"的错误。
因此,为了在
C++程序中使用用C写成的库,或在C++中尽可能的支持C,使用已经存在的C库,就需要用extern "C"来告诉编译器这是一个用C写成的库文件,并用C的方式来链接它们。

 

main.cpp修改如下:

#include "stdio.h"

 

extern "C"

{

#include "add.h"

extern int addition(int a,int b);

}

 

int main()

{

int a,b,c;

a=100;

b=200;

c=addition(a,b);

printf("the result is %d/n",c);

 

return 0;

}

 

编译、链接均能成功。

 

[root@yu28 testexternc4]# gcc -o add.o -c add.c

[root@yu28 testexternc4]# gcc -o main.o -c main.cpp

[root@yu28 testexternc4]# g++ main.o add.o -o main

[root@yu28 testexternc4]# ls

add.c  add.h  add.o  main  main.cpp  main.o

[root@yu28 testexternc4]# ./main

input a:100

input b:200

the result is 300

[root@yu28 testexternc4]#

 

7. 小结

  • C++中,为了支持重载机制,在编译生成汇编代码时,函数的名字要加入函数的参数类型或返回值类型等信息。
  • C中,因没有重载机制,编译后的代码只是简单的函数名字而已,不加入其他的信息。
  • C++程序中使用用C语言的代码,在编译时编译器是按照C++的方式来处理C的函数的,但实际上链接的库文件却是用C的方式来处理该函数的,因此编译器不会找到C语言的函数,从而出现"undefined reference to 'addition(intuit)'"的错误。
  • 因此,为了在C++程序中使用用C写成的库,或在C++中尽可能的支持C,使用已经存在的C库,就需要用extern "C"来告诉编译器这是一个用C写成的库文件,并用C的方式来链接它们。
  • gcc根据源文件的后缀来确定代码所使用的语言以及默认要链接的库。
  • 使用gcc链接C++程序时需要加上-lstdc++选项。

原文链接:http://blog.csdn.net/livelylittlefish/article/details/4471591
加载中
返回顶部
顶部