探索C++的秘密之一详解extern "C"

学习总结:C++extern “C”含义深层探索.doc 看看就知道:学习总结:C++extern “C”含义深层探索.doc 立即下载

时常在cpp的代码之中看到这样的代码:
 

#ifdef __cplusplus
extern "C" {
#endif

//一段代码

#ifdef __cplusplus
}

#endif


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

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

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

int f(void)
{
    
return 1;
}


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

    .file    "test.cxx"
    .text
    .align 
2
.globl _f
    .def    _f;    .scl    
2;    .type    32;    .endef
_f:
    pushl    
%ebp
    movl    
%esp, %ebp
    movl    $
1%eax
    popl    
%ebp
    ret


但是不加入了extern "C"之后
 

 .file "test.cxx"
 .text
 .align 
2
.globl __Z1fv
 .def __Z1fv; .scl 
2; .type 32; .endef
__Z1fv:
 pushl 
%ebp
 movl 
%esp, %ebp
 movl $
1%eax
 popl 
%ebp
 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写成的:

//f1.c
extern "C"
{
void f1()
{
    
return;
}

}

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

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

int main()
{
    f1();

    
return 0;
}


通过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++中使用这个库文件,我们需要这样写:

extern "C"
{
#include 
"f.h"
}

回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:
 

extern "C"
{
extern void f1();
}


int main()
{
    f1();

    
return 0;
}


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

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

C++学习——智能指针&&关键字 C++学习——智能指针&&关键字智能指针auto_ptrunique_ptrshared_ptrC++关键字 智能指针 三类智能指针模板都定义了类似指针的对象,可将new获得(直接或间接)的地址赋给该对象,过期时,其析构函数调用delete释放内存(过期自动释放) 使用智能指针必须包含#include<memory> 建议:1-每种指针都有不同的使用范围,unique_ptr指针优于其它两种类型,除非对象需要共享时用shared_ptr。 2- 建议– 如 阅读详情

相关推荐

探索C++秘密之一详解extern "C"

       时常在cpp的代码之中看到这样的代码:#ifdef __cplusplusextern "C" {#endif//一段代码#ifdef __cplusplus}#endif        这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加

技术资料库 1398

C++中rand()函数的用法

C++中rand()函数的用法

风暴计划 23万+

在DLL中获取智能指针的方法

编写DLL时,因为extern "C"的缘故,无法直接使用智能指针,不然会出现错误error C2526。 error C2526: “XXXX”: C 链接函数无法返回 C++ 类“std::shared_ptr<CEye>” 需要使用智能指针需要使用typedef对智能指针进行名称的转变,如下: typedef std::shared_ptr<CEye> MyClassCEyePtr; typedef std::shared_ptr<Gdiplus::Bi...

qq_40909394的博客 1839

探索C++秘密详解extern &quot;C&quot;

时常在cpp的代码之中看到这样的代码: #ifdef __cplusplus extern "C" { #endif //一段代码 #ifdef __cplusplus } #endif   这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,

lizhifeng2009的专栏 532

(转载)探索C++秘密详解extern &quot;C&quot;

219

前向声明 智能指针_c++是否应该使用智能指针,避免使用裸指针?

首先我认同这种观点,尽量避免使用Raw pointer,能用Smart pointer 就用Smart Pointer第一个思考,什么情况下该使用指针?答:现在好多c++程序员,存在指针滥用的情况。有人看我写c++代码,看我声明的都是对象,一脸鄙视的看着我,为什么不使用指针,指针效率高。 可事实是大部分情况是不必要使用指针的,大量的使用new/delete 可能只是为了更有C style,对 c范...

weixin_39898854的博客 300

extern"C"的使用

通常,在C语言的头文件中经常可以看到类似下面这种形式的代码那么,这种写法什么用呢?实际上,这是为了让CPP能够与C接口而采用的一种语法形式。之所以采用这种方式,是因为两种...

Linux知识积累 1万+

linux c 一站式学习 extern关键词作用、变量的声明与定义以及extern与include的区别(都与extern有关)

extern的大致有两个作用,就是后面加不加C。 一,后面加C如: extern "C" void fun(int a, int b);就是告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非。       二,不加C修饰变量和函数 如extern int g_Int;就是声明的这个变量或函数在在其他文件中

qq_21141511的博客 602

extern C的作用详解

     extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。      这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代

疯狂算法~ 14万+

linux extern "C"

1.引言C++语言的创建初衷是“a betterC”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言,为了支持函数的重载,C++对全局函数的处理方式

xzdwfwt111的专栏 4256

C++——C++基础】

本文系统介绍了C++中的命名空间、输入输出、缺省参数、函数重载与引用机制。命名空间有效避免标识符冲突,通过namespace和::实现作用域隔离;C++输入输出流(cin/cout)基于std命名空间,支持自动类型识别;缺省参数需从右向左连续定义,且声明中指定默认值;函数重载通过形参差异实现多态;引用作为变量别名,无额外内存开销,广泛用于传参与返回值,提升效率,但不可改变指向,且不能返回局部变量引用。

SHARK_pssm的博客 173

顺序表(Sequential List)详解:从数组到 C 语言实现

顺序表是用连续存储单元依次存储数据元素的线性结构,数组则是其最常见的实现方式,两者本质相同、仅视角不同——数组关注物理存储,顺序表关注逻辑关系。顺序表支持随机访问 $O(1)$,但插入删除需移动元素,效率为 $O(n)$。本文用 C 语言静态数组完整实现了顺序表的初始化、增删改查操作,帮助初学者在不涉及指针的前提下理解其核心原理,为后续学习动态顺序表打下基础。

Guangyu536的博客 295

C++】 定时器进阶:多层时间轮原理与任务迁移

单层时间轮虽然能够以较低的开销管理大量定时任务,但当任务延迟时间跨度很大时,要么需要大量槽位,要么需要额外维护轮数。多层时间轮通过将不同时间范围的任务放到不同层级中,实现类似“秒针、分针、时针”的分级管理。本文主要介绍多层时间轮为什么出现、各层如何划分、定时任务如何选择层级,以及高层任务如何逐步迁移到低层并最终执行。

qq_64148519的博客 114

​​​​​​​第 4 节 如何用 C++ 设计一个 AI 推理 SDK 的核心接口

AI推理SDK核心接口应遵循“环境→模型→执行器→张量”四层抽象,采用不透明指针保证ABI稳定,以C API为底座、C++ API为封装,统一错误处理,并权衡张量、内存、异步、线程安全与插件扩展。参考ONNXRuntime、TensorRT、OpenVINO设计,实现易用、高性能、可扩展的工业级接口。

Question_123456的博客 515

STM32 USART 详解(八):C I/O 串口重定向原理与实现

本文深入解析STM32串口重定向C标准I/O的核心原理与实现。文章从printf/scanf底层机制入手,剖析数据流向,并提供fputc与fgetc的完整重定向代码。同时深度解析换行符及输入机制问题,最后通过完整测试验证数据流~

2502_91499791的博客 130
上一篇: 玩转黑莓8900,不信你不会。超级实用
下一篇: 探索C++的秘密之二:重载,覆盖,和隐藏
jun2ran
博客等级 码龄19年 102粉丝 183原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值