`
xitongyunwei
  • 浏览: 927047 次
文章分类
社区版块
存档分类
最新评论

extern “C”

 
阅读更多

在很多C/C++头文件中,你可能会经常看到使用extern “C”{}包含起来的代码。这种语法的存在是为了使C和C++代码能够兼容。兼容?C++对C本来不就是兼容的吗?确实,在语法层面,C++支持几乎所有C语法。但这里的兼容是目标文件或者库级别的兼容,包括两种情况下的兼容。一种是,在C++代码中调用已经存在的C代码写成的库;另一种是,使用C编写代码,而希望这些代码能够使C++可以调用。
  若想继续讨论extern “C”,就不得不提name mangling。
  我们知道,相比C,C++支持函数重载,支持命名空间,支持类以及成员函数。函数重载允许我们声明名字相同参数不同的函数,命名空间(namespace)和类为名字(或者说符号)划分了不同的域使得我们可以在不同域中使用完全相同的名字。而为了实现这种特性,C++就使用了name mangling。即是说,C++编译器在编译C++文件时,把每一个函数名“打乱”成与函数所在域、参数类型、调用约定相关的另外一个名字(因为汇编级别的代码中,所有符号都在同一个域)。由于C中并不存在这些特性,所以通常C编译器并不对符号进行mangling。
  由于C++中这种mangling的存在,使C和C++在符号上不统一,无法直接相互调用。比如C中有这样一个函数,


//~ add.h
int add(int, int);
//~ add.c
int add(int a, int b)
{
return a + b;
}
使用gcc编译add.c。然后在C++代码中直接调用add函数,


//~ main.cpp
#include <add.h>
int main()
{
int a = add(0, 1);
return 0;
}
这样编译main.cpp,


$ gcc -c add.c
$ g++ -c main.cpp
$ g++ main.o add.o -o main
main.o: In function `main':
main.cpp:(.text+0x13): undefined reference to `add(int, int)'
collect2: ld returned 1 exit status
错误提示add(int, int)为找到。将main.cpp编译成汇编后,查看调用add的代码为


movl $1, %esi # 参数入栈
movl $0, %edi # 参数入栈
call _Z3addii # 调用add, 注意符号
movl %eax, -4(%rbp) # 返回值赋给int a
  解决这种情况的方法就是使用extern “C”,在add.h中,把add的声明放入extern “C”{}中。这样,g++就不会对add进行mangling,链接器就会在add.o中找到add并链接到main.o。通常,C代码的头文件(包括libc的头文件,比如string.h)中,函数的声明通常是这样的,


//~ add.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int, int);
#ifdef __cplusplus
}
#endif
这样一来,如果是C代码包含add.h,那么将是直接包含add函数的声明。如果是C++代码包含add.h,那么extern “C”就将会被引入。关于name mangling,请参考这里,还有这里。
转载自:
http://www.dutor.net/index.php/2010/09/extern-c/

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics