C/C++分享点:extern 与extern "C"

C/C++分享点:extern 与extern “C”

1.exern 的作用

1.1 引用同一个文件中的变量

看一段测试代码:

#define _CRT_SECURE_NO_WARNING 1

#include<stdio.h>

int func();

int main()
{
	func();
	printf("%d\n", num);
    return 0;
}

int num = 4;

int func()
{
    printf("%d\n",num);
	return num;
}

编译会发现编译器报错!!!

如果按照这个顺序,变量 num在main函数的后边进行声明和初始化的话,那么在main函数中是不能直接引用num这个变量的,因为当编译器编译到这一句话的时候,找不到num这个变量的声明,但是在func函数中是可以正常使用,因为func对num的调用是发生在num的声明和初始化之后。
如果我不想改变num的声明的位置,但是想在main函数中直接使用num这个变量,怎么办呢?
可以使用extern这个关键字。
像下面这一段代码,利用extern关键字先声明一下num变量,告诉编译器num这个变量是存在的,但是不是在这之前声明的,你到别的地方找找吧,果然,这样就可以顺利通过编译啦。但是你要是想欺骗编译器也是不行的,比如你声明了extern int num;但是在后面却没有真正的给出num变量的声明,那么编译器去别的地方找了,但是没找到还是不行的。
修正后的代码

#define _CRT_SECURE_NO_WARNING 1

#include<stdio.h>

int func();

int main()
{
	extern int num;
	func();
	printf("%d\n", num);
    return 0;
}

int num = 4;

int func()
{
    printf("%d\n",num);
	return num;
}

1.2 引用另一个文件中的变量

如果extern这个关键字就这点功能,那么这个关键字就显得多余了,因为上边的程序可以通过将num变量在main函数的上边声明,使得在main函数中也可以使用。
extern这个关键字的真正的作用是引用不在同一个文件中的变量或者函数。

main.c

#include<stdio.h>

int main()
{
    extern int num;
    printf("%d",num);
    return 0;
}
fun.c

#include<stdio.h>

int num = 5;

void func()
{
    printf("fun in a.c");
}

这里b.c中定义了一个变量num,如果main.c中想要引用这个变量,那么可以使用extern这个关键字,
注意这里能成功引用的原因是,num这个关键字在b.c中是一个全局变量,也就是说只有当一个变量是一个全局变量时,extern变量才会起作用,向下面这样是不行的。
main.c

#include<stdio.h>

int main()
{
    extern int num;
    printf("%d",num);
    return 0;
}

fun.c

#include<stdio.h>

void func()
{
    int num = 5;
    printf("fun in a.c");
}

extern关键字只需要指明类型和变量名就行了,不能再重新赋值,初始化需要在原文件所在处进行,如果不进行初始化的话,全局变量会被编译器自动初始化为0。像这种写法是不行的。
extern int num=4;
但是在声明之后就可以使用变量名进行修改了,像这样

#include<stdio.h>

int main()
{
    extern int num;
    num=1;
    printf("%d",num);
    return 0;
}

当然了,如果不想修改还可以使用const修饰
使用include将另一个文件全部包含进去可以引用另一个文件中的变量,但是这样做的结果就是,被包含的文件中的所有的变量和方法都可以被这个文件使用,这样就变得不安全,
如果只是希望一个文件使用另一个文件中的某个变量还是使用extern关键字更好。
引用另一个文件中的函数
extern除了引用另一个文件中的变量外,还可以引用另一个文件中的函数,引用方法和引用变量相似。

当然了,如果不想修改还可以使用const修饰
使用include将另一个文件全部包含进去可以引用另一个文件中的变量,但是这样做的结果就是,被包含的文件中的所有的变量和方法都可以被这个文件使用,这样就变得不安全,
如果只是希望一个文件使用另一个文件中的某个变量还是使用extern关键字更好。

1.3 引用另一个文件中的函数

extern除了引用另一个文件中的变量外,还可以引用另一个文件中的函数,引用方法和引用变量相似。

main.c

#include<stdio.h>

int main()
{
    extern void func();
    func();
    return 0;
}

fun.c

#include<stdio.h>

const int num=5;
void func()
{
    printf("fun in a.c");

}

2 extern 引用函数

2.1.extern 关键字放在函数声明之前

如在test.h

extern int test();

如果这样函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义

在程序中取代include “*.h”来声明函数.

在一些复杂的项目中,比较习惯在所有的函数声明前添加extern修饰,以防止遗漏包含头文件而导致的编译错误。

2.2 extern 关键字放在函数定义之前

如在test.c

extern int test()

{

return true;

}

## 

如果在函数定义的地方带有关键字extern,表示该函数会提供给外部文件使用,其实有些编译器是默认每个函数都是extern类型的,反之是static类型

3 c和c++相互调用:extern “C”

C调用C++或者C++调用C意思是.c文件中调用.cpp文件中代码,或者相反。

首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在#include的地方展开一下,没别的意义了,其实头文件主要是给别人看的。

做一个实验,将头文件的后缀改成xxx.txt,然后在引用该头文件的地方用

#include"xxx.txt"

编译,链接都很顺利的过去了,由此可知,头文件仅仅为阅读代码作用,没其他的作用了!

#ifdef __cplusplus
extern "C" {
#endif

void *memset(void* ,int , size_t);

#ifdef __cplusplus
}
#endif

__cplusplus关键字 和 extern “C”两种关键字,都是为了实现C++与C兼容的,extern “C”是用来在C++程序中声明或定义一个C的符号。

上面的代码,C++编译器会将在extern “C”的大括号内部的代码当做C语言来处理。

由于C和C++毕竟是不同的,为了实现某个程序在C和C++中都是兼容的,如果定义两套头文件,未免太过麻烦,所以就有了__cplusplus的出现,这个是在C++中特有的,__cplusplus其实就是C++,也就有了上面第一段代码的使用,如果这段代码是在C++文件中出现,那么经过编译后,该段代码就变成了:

3.1 C代码中如何调用C++

使用extern "C" 主要是因为C编译器编译函数时不带参数的类型信息,只包含函数的符号名字。如
int foo( float x )
C编译器会将此函数编译成类似_foo的符号,C连接器只要找到了调用函数的符号,就认为连接成功。
而C++编译器为了实现函数重载,会在编译时带上函数的参数信息。如它可以把上面的函数编译成类似于_foo_float这样的符号。
所以,C调用C++,使用extern "C"则是告诉编译器依照C的方式来编译封装接口,当然接口函数里面的C++语法还是按C++方式编译。
如:1 普通函数

// C++ Code
extern "C" int foo( int x );
int foo( int x )
{
   //...
}

这样,编译器会将foo函数编译成类似_foo符号,而不会编译成类似_foo_int符号
则C可以这样调用C++函数

// C Code
int foo( int x );
void cc( int x )
{
    foo( x );
    //...
}

2 如果想调用重载的C++函数,则须封装单独的接口共C调用。

// C++ Code
void foo( int x );
void foo( float x );
extern "C" void foo_i( int x )
{
    foo( x );
}
extern "C" void foo_f( float x )
{
    foo( x );
}

则C中可这样调用

// C Code
void foo_i( int x );
void foo_f( float x );
void ccc( int x1, float x2 )
{
    foo_i( x1 );
    foo_f( x2 );
    // ...
} 

C中想调用C++中的成员函数(包括虚函数),则需要提供一个简单的包装(wrapper)。

例如: // C++ code:

class C

{

  ...

  virtual double f(int);

};

extern "C" double call_C_f(C* p, int i) // wrapper function

{

return p->f(i);

}

然后,你就可以这样调用 C::f():

//C code

double call_C_f(struct C* p, int i);//声明

void ccc(struct C* p, int i)

{

   double d=call_C_f(p,i);

 ...

}

问题:参数struct C* p从哪里来,即怎么在C中定义C++对象,其实上面只是说了思想,真实的c中使用C++类需要把原来的类都封装一下,参看下面的文章如何用C语言封装 C++的类,在 C里面使用

3.2 C++中如何调用C

而C++调用C,extern “C” 的作用是:让C++连接器找调用函数的符号时采用C的方式 如:
// C Code
void foo( int x );

C++这样调用C函数
// C++ Code
extern “C” void foo( int x );

就是让C++连接器能过类似于_foo来查找此函数,而非类似于_foo_int这样的符号。

时常在cpp的代码之中看到这样的代码: 特别是C ++中引入C的头文件,这些C头文件中出现很多如下代码。

#ifdef __cplusplus extern "C" { #endif

//一段代码

#ifdef __cplusplus } #endif  

其中__cplusplus是C++编译器的保留宏定义.就是说C++编译器认为这个宏已经定义了.

所以关键是extern "C" {}

extern "C"是告诉C++编译器件括号里的东东是按照C的obj文件格式编译的,要连接的话按照C的命名规则去找.

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

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

试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.

现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

extern "C" {

#include "f.h"

}

参考文献

如何用C语言封装 C++的类,在 C里面使用

C语言中的extern关键字的使用

__cplusplus、extern “C”关键字

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值