extern "C"学习笔记

C

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

extern "C"的由来

由于C和C++编译器生成的目标文件中对函数名和全局变量名的修饰规则不同(如C++支持函数重载,而C不支持),为了兼容性,C++标准制定者创造了extern “C”,故该用语只能用于C++编译器,不能用于C编译器。
extern “C”(C必须大写)是连接申明(linkage declaration),申明的结果是在编译后的目标文件中,按照C编译器的命名修饰规则来修饰被extern "C"申明的变量名和函数名。

//file.c
#include <stddef.h>
struct  strA
{
	float a;
	int   b;
} bb[3] = {{1.1, 2}, {3.4, 5}, {4.5 ,7}};
int* iptr[100] = {NULL};
double dss = 1001.01;

//extern "C"
int cfunc(float* fa, int ib) 
{
	return 0;
}

第三次是使用extern “C编译后结果”
第三次是去除了extern “C前注释符后的编译结果。
由上图可见,gcc和g++对全局变量在目标文件中的命名修饰规则是相同的(据了解,某些编译器如Microsoft Visual VC/C++ 6.0对全局变量修饰规则不同),即extern "C"实际只作用于函数名,全局变量并不需要extern "C"特殊申明。建议按照惯例将全局变量和函数声明都使用extern "C"申明,至少不会出现问题。

extern "C"的目的

使C++编译器按照c编译器的修饰规则来修饰相应的函数名和全局变量名,以便于C和C++混合编程(如c++调用c函数),而不是使用C编译器编译被extern "C"申明的函数体和全局变量,即函数体中依然可以使用cout、class等C++才有的内部名称,甚至可以使用extern "C"申明的函数名在extern "C"作用域外实现函数重载;全局变量的定义仍然遵循C++规范。

extern "C"的用法

一般的将extern “C” 用于函数和全局变量声明部分,以消除c++编译器给目标名添加的特殊命名修饰,建议放在头文件里, 也可以在模块文件中定义处使用。extern “C”{ }作用于花括号内所有声明和定义,如果没有花括号,则只作用于紧跟在后面(同一行或下一行)的一个声明或定义,举例如下:

 //单个声明,extern具有双重含义,修饰"C", 同时修饰被声明的对象
 //故类型前面不需要也不能再添加extern
extern "C"   float   R;                         //声明

//编译警告:初始化和extern同时出现,其他地方再有定义,报错重复定义
extern "C"  int  a = 0;	     //定义,忽略extern

extern "C"  struct  aa{
	float  a, b;
	}strfor_;					        //声明
extern "C"  void  c_fun(float*);        //声明
extern "C"  
 void  c_fun( float* a)  { ...  }       //定义

//用于多个声明,可以包括函数、变量定义,extern不再具有双重含义
extern "C"
{
	#include  “test.h”                  //应用于test.h中的所有声明和定义
	float  zzz = 234.9;      //定义, 无初始值时,也是定义
	extern int  out1;      //声明
	extern  void  c_fun(float*);        //声明
	void  c_fun1( float* a)  { ...  }    //定义, 不建议
}

使用c++预定义宏__cplusplus

 编译器和文件扩展名有如下组合
g++  -c  file.c
g++  -c  file.cpp
gcc   -c  file.cpp
gcc  -c  file.c

前三种情况下,都是使用的c++编译器,即预定义了宏__cplusplus ;第四种情况使用的c编译器,即宏 __cplusplus 未定义,且 extern "C"不适用于这种情况。为了适用各种情况,可以利用该宏做如下处理

#ifdef  __cplusplus 
extern "C"  {
#endif
    ...............          //声明和定义, 如果一行搞定,可以去除{ }
#ifdef  __cplusplus 
}
#endif  

对于前三种情况,#ifdef __cplusplus 是多余的;对于第四种情况,如果没有#ifdef __cplusplus,无法通过编译,所以包括extern “C” 都是多余的。当完全确定使用的何种方式时,不使用#ifdef __cplusplus 是可以的;当不确定使用何种编译器时,建议使用#ifdef __cplusplus 一劳永逸。

另外:
1、在头文件中使用extern “C”,必须使用{},即使是单一声明;
2、当混合编程时,假如fortran中定义了a,在C文件中声明时,extern int a_ = 10 或 int a_ = 10,或int a_;都不会报错,前者编译时有警告,三者在链接时都有alignment警告。尽管a和a_指向相同的内存,编译后就是同一个名称a_,但由于c中赋值的“强制性”优先于extern,所以在这三种情况下,都把a_当做(假定)变量定义,由于c和fortran在字节对齐方面的差别,就有了alignment警告。因此最好还是使用extern int a_,明确声明为外部全局变量,并避免了警告和实际执行中可能产生的错误。

部分参考文件
extern “C”的作用详解
在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?----转

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值