免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 8226 | 回复: 27
打印 上一主题 下一主题

[C++] 探索C++的秘密之二:重载,覆盖,和隐藏 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-01-15 00:02 |只看该作者 |倒序浏览
探索C++的秘密之一详解extern "C"
       时常在cpp的代码之中看到这样的代码:


  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif

  4. //一段代码

  5. #ifdef __cplusplus
  6. }
  7. #endif
复制代码



        这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码.

        要明白为何使用extern "C",还得从cpp中对函数的重载处理开始说起.在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的参数类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.

         比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:

  1. int f(void)
  2. {
  3.     return 1;
  4. }
复制代码


       在加入extern "C"的时候产生的汇编代码是:


  1.     .file    "test.cxx"
  2.     .text
  3.     .align 2
  4. .globl _f
  5.     .def    _f;    .scl    2;    .type    32;    .endef
  6. _f:
  7.     pushl    %ebp
  8.     movl    %esp, %ebp
  9.     movl    $1, %eax
  10.     popl    %ebp
  11.     ret
复制代码

但是不加入了extern "C"之后
  1.         .file        "test.cxx"
  2.         .text
  3.         .align 2
  4. .globl __Z1fv
  5.         .def        __Z1fv;        .scl        2;        .type        32;        .endef
  6. __Z1fv:
  7.         pushl        %ebp
  8.         movl        %esp, %ebp
  9.         movl        $1, %eax
  10.         popl        %ebp
  11.         ret
复制代码

        两段汇编代码同样都是使用gcc -S命令产生的,所有的地方都是一样的,
唯独是产生的函数名,一个是_f,一个是__Z1fv.

        OK,明白了加入与不加入extern "C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern "C"呢?
        C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略.

       试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码.如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

  1. //f1.c
  2. extern "C"
  3. {
  4. void f1()
  5. {
  6.     return;
  7. }
  8. }
复制代码

编译命令是:gcc -c f1.c -o f1.o   产生了一个叫f1.o的库文件.
再写一段代码调用这个f1函数:

  1. // test.cxx
  2. //这个extern表示f1函数在别的地方定义,这样可以通过
  3. //编译,但是链接的时候还是需要
  4. //链接上原来的库文件.
  5. extern void f1();

  6. int main()
  7. {
  8.     f1();

  9.     return 0;
  10. }
复制代码

通过gcc -c  test.cxx -o test.o 产生一个叫test.o的文件.
然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是:
test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'
也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数.
      因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们.
      比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

  1. extern "C"
  2. {
  3. #include "f.h"
  4. }
复制代码

回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:
  1. extern "C"
  2. {
  3. extern void f1();
  4. }

  5. int main()
  6. {
  7.     f1();

  8.     return 0;
  9. }
复制代码

重新编译并且链接就可以过去了.

总结:C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明.

[ 本帖最后由 converse 于 2006-3-24 20:33 编辑 ]

评分

参与人数 1可用积分 +1 收起 理由
win_hate + 1

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2006-01-15 00:31 |只看该作者
解释得很明白,楼主辛苦啦

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
3 [报告]
发表于 2006-01-15 10:25 |只看该作者
解释的很清楚,解释了why ,how ,when。兄弟辛苦啦

论坛徽章:
0
4 [报告]
发表于 2006-01-15 12:37 |只看该作者
永远支持员创!

论坛徽章:
0
5 [报告]
发表于 2006-01-15 18:51 |只看该作者
支持!!!

论坛徽章:
0
6 [报告]
发表于 2006-01-16 08:42 |只看该作者
顶一下

论坛徽章:
0
7 [报告]
发表于 2006-01-16 11:07 |只看该作者
赞一个!原创就是好!

论坛徽章:
0
8 [报告]
发表于 2006-01-16 12:50 |只看该作者
这个应该成为一种习惯

论坛徽章:
0
9 [报告]
发表于 2006-01-16 16:15 |只看该作者
高山仰止啊

论坛徽章:
0
10 [报告]
发表于 2006-01-16 17:38 |只看该作者
写得很好哈
楼主继续加油:)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP