C++ 链接指示:extern “C“

链接指示: extern "C"

参考:《C++ Primer 中文版(第5版)第Ⅳ部分 高级主题 第19章 特殊工具与技术 19.8 固有的不可移植的特性 19.8.3 链接指示:extern “C”

**链接指示(linkage directive):**支持C++程序调用其他语言编写的函数的一种机制。所有编译器都应支持调用C++和C函数,至于是否支持其他语言则由编译器决定。

其他语言中的函数名字也必须在C++中进行声明,并且该声明必须指定返回类型和形参列表。对于其他语言编写的函数来说,编译器检查其调用的方式与处理普通C++函数的方式相同,但是生成的代码有所区别。C++使用 链接指示 指出任意非C++函数所用的语言。

要想把C++代码和其他语言(包括C语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前C++编译器是兼容的。

声明一个非C++的函数

链接指示可以有两种形式:单个的或复合的。链接指示不能出现在类定义或函数定义的内部。链接指示必须在函数的每个声明中都出现。

//单语句链接指示
extern "C" size_t strlen(const char*);

//复合语句链接指示
extern "C"
{
    int strcmp(const char*, const char*);
    char* strcat(char*, const char*);
}

链接指示的第一种形式包含一个关键字extern,后面是一个字符串字面值常量以及一个“普通的”函数声明。

其中的字符串字面值常量指出了编写函数所用的语言。编译器应该支持对C语言的链接指示。此外,编译器也可能会支持其他语言的链接指示。

链接指示与头文件

第二种形式用花括号括起来若干个函数的声明,从而一次性建立多个链接。花括号的作用是将适用于该链接指示的多个声明聚合在一起,花括号中声明的函数名字就是可见的,就好像在花括号之外声明的一样。

多重声明的形式可以应用于整个头文件:

//复合语句链接指示
extern "C"
{
#include <string.h>  操作C风格字符串的C函数
}

当一个#include指示被放置在符合链接指示的花括号中时,头文件中的所有普通函数声明都被认为是由链接指示的语言编写的。链接指示可以嵌套,因此如果头文件包含自带链接指示的函数,则该函数的链接不受影响。

C++从C语言继承的标准库函数可以定义成C函数,但并非必须。

指向 extern "C"函数的指针

编写函数所用的语言时函数类型的一部分。因此,对于使用链接指示定义的函数来说,它的每个声明都必须使用相同的链接指示。而且,指向其他语言编写的函数的指针必须与函数本身使用相同的链接指示:

//pf指向一个C函数,该函数接受一个int返回void
extern "C" void (*pf)(int);

当使用pf调用函数时,编译器认定当前调用的是一个C函数。

指向C函数的指针与指向C++函数的指针是不一样的类型。如果试图在两个链接指示不同的指针之间进行赋值操作,则程序将发生错误:

void (*pf1)(int);             //指向一个C++函数
extern "C" void (*pf2)(int);  //指向一个C函数
pf1 = pf2;                    //错误:pf1和pf2的类型不同

有的C++编译器会接受之前的这种赋值操作并将其作为对语言的扩展,尽管从严格意义上来看它是非法的。

链接指示对整个声明都有效

使用链接指示时,它不仅对函数有效,而且对作为返回类型或形参类型的函数指针也有效:

//f1是一个C函数,它的形参是一个指向C函数的指针
extern "C" void f1(void(*)(int));

f1是一个不返回任何值的C函数。这个链接指示不仅对f1有效,对函数指针同样有效。当调用f1时,必须传给它一个C函数的名字或者指向C函数的指针。

因为链接指示同事作用于声明语句中的所有函数,所以如果希望给C++函数传入一个指向C函数的指针,则必须使用类型别名:

//FC是一个指向C函数的指针
extern "C" typedef void FC(int);
//f2是一个C++函数,该函数的形参是指向C函数的指针
void f2(FC*);

导出C++函数到其他语言

通过使用链接指示对函数进行定义,可以令一个C++函数在其他语言编写的程序中可用:

//calc函数可以被C程序调用
extern "C" double calc(double dparm) { /* ... */ }

编译器将为该函数生成适合于指定语言的代码。

可被多种语言共享的函数的返回类型或形参类型受到很多限制。例如,不太可能把一个C++类的对象传递给C程序,因为C程序根本无法理解构造函数、析构函数以及其他类特有的操作。

对链接到C的预处理器的支持:有时需要在C和C++中编译同一个源文件,为了实现这一目的,在编译C++版本的程序时预处理器定义__cplusplus。利用这个变量,可以在编译C++程序的时候有条件地包含进来一些代码:

#ifdef __cplusplus
//正确:正在编译C++程序
extern "C"
#endif
int strcmp(const char*, const char*);

重载函数与链接指示

链接指示与重载函数的相互作用依赖于目标语言。如果目标语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持重载这些C++函数。

C语言不支持函数重载,所以一个C链接指示只能用于说明一组重载函数中的某一个:

//错误:两个extern "C"函数的名字相同
extern "C" void print(const char*);
extern "C" void print(int);

如果在一组重载函数中有一个是C函数,则其余的必定都是C++函数:

class SmallInt { /* ... */ };
class BigNum { /* ... */ };
//C函数可以在C或C++程序中调用
//C++函数重载了该函数,可以在C++程序中调用
extern "C" double calc(double);
extern SmallInt calc(const SmallInt&);
extern BigNum calc(const BigNum&);

C版本的 calc 函数可以在C或C++程序中调用,而使用了类类型形参的C++函数只能在C++程序中调用。上述性质与声明的顺序无关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值