对extern “C"的理解

参考:

http://www.cnblogs.com/rollenholt/archive/2012/03/20/2409046.html

 

被extern "C"修饰的变量和函数是按照C语言方式编译和连接的,实现C++与C的混合编程。

 

下面做几个实验来理解:

 

C++和C编译时对函数名的处理

下面是一个C++文件, a.cpp

void func1(void)
{}

void func2(int a)
{}

void func3(int a, int b)
{}

int func4(void)
{}

int func5(int a)
{}

int func6(int a, int b)
{}

int main(int argc, const char *argv[])
{
        func1();
        func2(1);
        func3(1, 2);
        func4();
        func5(1);
        func6(1, 2);
        return 0;
}

 
使用命令  g++ -S a.cpp 编译这个文件,得到编译后得到的汇编文件 a.s,摘录部分内容如下:

 1 .LFB1026:
 2         .cfi_startproc
 3         pushq   %rbp
 4         .cfi_def_cfa_offset 16
 5         .cfi_offset 6, -16
 6         movq    %rsp, %rbp
 7         .cfi_def_cfa_register 6
 8         subq    $16, %rsp
 9         movl    %edi, -4(%rbp)
10         movq    %rsi, -16(%rbp)
11         call    _Z5func1v
12         movl    $1, %edi
13         call    _Z5func2i
14         movl    $2, %esi
15         movl    $1, %edi
16         call    _Z5func3ii
17         call    _Z5func4v
18         movl    $1, %edi
19         call    _Z5func5i
20         movl    $2, %esi
21         movl    $1, %edi
22         call    _Z5func6ii
23         movl    $0, %eax
24         leave
25         .cfi_def_cfa 7, 8
26         ret
27         .cfi_endproc
28 .LFE1026:

 

可以看到,将函数名分别进行了如下处理:

void func1(void)                ---> _Z5func1v
void func2(int a)               ---> _Z5func2i
void func3(int a, int b)       ---> _Z5func3ii
int  func4(void)                 ---> _Z5func4v
int  func5(int a)                 ---> _Z5func5i
int func6(int a, int b)         ---> _Z5func6ii

可以看到,将来参数类型会和函数名结合,从而产生新的标示,与返回值的类型没有关系。这就解释了C++允许函数重载的原因,并且知道了为什么仅返回值类型不同不能认为是重载的原因。

还是这个文件,我们重命名为 b.c,然后使用命令  gcc -S b.c,得到汇编文件 b.s,摘录部分内容如下:

main:
.LFB6:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        call    func1
        movl    $1, %edi
        call    func2
        movl    $2, %esi
        movl    $1, %edi
        call    func3
        call    func4
        movl    $1, %edi
        call    func5
        movl    $2, %esi
        movl    $1, %edi
        call    func6
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

 

可以看到,在编译过程中,函数的名称并没有改变,这也就解释了C语言为什么不支持函数的重载。

 

那么,既然C和C++在编译时对函数名的处理不同,那么如何实现C和C++的混合编程呢?即在C中调用C++的函数,在C++中调用C的函数。

先看一下现象:

a.cpp

int main(int argc, const char *argv[])
{
        func1();
        return 0;
}

使用命令 g++ -c a.cpp,出现如下错误:

a.cpp: In functionint main(int, const char**)’:
a.cpp:3:8: error: ‘func1’ was not declared in this scope
  func1();
        ^

原因是我们没有声明func1这个函数,我发现,如果是在C语言中,不声明并不会导致编译错误,原因其实很简单,在C++中,编译的时候给函数设置新的标示符的时候要用到函数的参数类型,如果不声明,编译器无法获得func1的参数类型信息,从而编译出错。而在C语言中,编译过程中函数的标识符就是函数名,不考虑参数信息,也就不会出错。

修改a.cpp如下:

extern func1(void);

int main(int argc, const char *argv[])
{
        func1();
        return 0;
}

 

相应的汇编部分内容如下:

main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        call    _Z5func1v
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

b.c

void func1(void)
{}

使用如下命令编译: gcc -c b.c

相应的汇编文件如下:

        .file   "b.c"
        .text
        .globl  func1
        .type   func1, @function
func1:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

 

然后使用命令 g++ a.o b.o 链接,提示如下错误:

a.o: In function `main':
a.cpp:(.text+0x10): undefined reference to `func1()'
collect2: error: ld returned 1 exit status

原因是, 在链接时,由于找不到_Z5func1v导致编译失败,解决办法如下:

修改a.cpp

extern "C" void func1(void);

int main(int argc, const char *argv[])
{
        func1();
        return 0;
}

我们看一下得到的汇编文件(g++ -S a.cpp):

main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        call    func1
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

可以看到,标识符已经从_Z5func1v变成了func1,即从C++的编译方式变成了C的编译方式,这样在链接时就不会出错了。

那么如果是在C语言中调用C++的函数,结果如何呢?

a.c

int main(int argc, const char *argv[])
{
        func1();
        return 0;
}

使用命令 gcc -c a.c编译得到a.o

得到的汇编文件如下:

main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $0, %eax
        call    func1
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

b.cpp

void func1(void)
{}

得到的汇编文件如下:

        .file   "b.cpp"
        .text
        .globl  _Z5func1v
        .type   _Z5func1v, @function
_Z5func1v:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

 

使用命令 g++ -c b.cpp编译得到b.o

使用命令gcc a.o b.o出现如下错误的提示

a.o: In function `main':
a.cpp:(.text+0x10): undefined reference to `func1'
collect2: error: ld returned 1 exit status

修改b.cpp

extern "C" void func1(void)
{}

注:这里不能修改a.c,因为C语言中无法识别extern "C"

得到的汇编如下(g++ -S b.cpp):

        .file   "b.cpp"
        .text
        .globl  func1
        .type   func1, @function
func1:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

这样在链接时就不会出错了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值