CC++中用extern修饰函数的老问题

CC++中用extern修饰函数的老问题,第1张

编译器肯定不会去查找什么“别的文件”,编译器只是针对一个文件进行编译的。这里用到的特性是, 如果一个函数调用实现没声明过,编译器就当这个函数参数类型跟调用时候给的类型是一样的,返回值就当是int, 这样编译就能通过, 而链接不一定能通过, 这要看函数的实际情况跟它假设的情况和使用情况是不是有冲突

另外说一下这个只是C语言的语法,C++不认这一套

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。

extern修饰变量的声明。举例来说,如果文件ac需要引用bc中变量int v,就可以在ac中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说ac要引用到v,不只是取决于在ac中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在ac中的任何地方,比如你可以在ac中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。

2 extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件ac需要引用bc中的函数,比如在bc中原型是int fun(int mu),那么就可以在ac中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在ac中任何地方,而不一定非要放在ac的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。

extern "C"的含义

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。

被extern "C"限定的函数或变量是extern类型的;

被extern "C"修饰的变量和函数是按照C语言方式编译和链接的

首先看看C++中对类似C的函数是怎样编译的。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo( int x, int y );

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。

_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。 例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以""来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

举例说明

(1)未加extern "C"声明时的连接方式

假设在C++中,模块A的头文件如下:

// 模块A头文件 moduleAh

#ifndef MODULE_A_H

#define MODULE_A_H

int foo( int x, int y );

#endif

//在模块B中引用该函数:

// 模块B实现文件 moduleBcpp

#include "moduleAh"

foo(2,3);

实际上,在连接阶段,链接器会从模块A生成的目标文件moduleAobj中寻找_foo_int_int这样的符号!

(2)加extern "C"声明后的编译和链接方式

加extern "C"声明后,模块A的头文件变为:

// 模块A头文件 moduleAh

#ifndef MODULE_A_H

#define MODULE_A_H

extern "C" int foo( int x, int y );

#endif

在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:

<1>A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

<2>链接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo(int x, int y),则模块B找不到模块A中的函数;反之亦然。

extern “C”这个声明的真实目的是为了实现C++与C及其它语言的混合编程。

extern 用于函数定义 表示全局可见的 用于变量 表示 他在其他 的地方 定义

要是在本函数定义 就是告诉 这个变量在任何地方可见 :

事实上 大多数 都没有在函数名 之前加 存储类型 说明符的习惯 所以大多数的函数 都是全局可见的

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/11670512.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-17
下一篇2023-05-17

发表评论

登录后才能评论

评论列表(0条)

    保存