Lit*_*rum 1511 c c++ name-mangling linkage extern-c
究竟什么extern "C"
放入C++代码呢?
例如:
extern "C" {
void foo();
}
Run Code Online (Sandbox Code Playgroud)
Fai*_*ali 1462
extern"C"使得C++中的函数名称具有"C"链接(编译器不会破坏名称),以便客户端C代码可以使用仅包含"C"兼容头文件来链接(即使用)您的函数.声明你的功能.您的函数定义包含在二进制格式(由C++编译器编译)中,客户端"C"链接器将使用"C"名称链接到该格式.
由于C++有函数名称的重载而C没有,因此C++编译器不能只使用函数名作为链接的唯一id,因此它通过添加有关参数的信息来破坏名称.AC编译器不需要破坏名称,因为您不能在C中重载函数名.当您声明函数在C++中具有extern"C"链接时,C++编译器不会将参数/参数类型信息添加到用于的名称连锁.
您知道,您可以明确指定每个单独的声明/定义的"C"链接,或使用块将一系列声明/定义分组以具有特定的链接:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
Run Code Online (Sandbox Code Playgroud)
如果您关心技术细节,它们列在C++ 03标准的7.5节中,这里是一个简短的摘要(重点是extern"C"):
Unc*_*lby 298
只是想添加一些信息,因为我还没有看到它发布.
您经常会在C标头中看到代码,如下所示:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)
这实现了它允许您将C头文件与C++代码一起使用,因为将定义宏"__cplusplus".但是你可以也仍然使用旧的C代码,其中宏使用NOT定义,所以它不会看到独特的C++构建.
虽然,我也看过C++代码,例如:
extern "C" {
#include "legacy_C_header.h"
}
Run Code Online (Sandbox Code Playgroud)
我想象的完成了同样的事情.
不确定哪种方式更好,但我已经看到了两种方式.
Cir*_*四事件 218
反编译g++
生成的二进制文件以查看发生的情况
main.cpp中
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Run Code Online (Sandbox Code Playgroud)
使用GCC 4.8 Linux ELF输出编译:
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
Run Code Online (Sandbox Code Playgroud)
反编译符号表:
8: 0000000000000000 7 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000e 17 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
Run Code Online (Sandbox Code Playgroud)
输出包含:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Run Code Online (Sandbox Code Playgroud)
解释
我们看到:
ef
并eg
存储在与代码中名称相同的符号中
其他符号被破坏了.让我们解开他们:
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
Run Code Online (Sandbox Code Playgroud)结论:以下两种符号类型都没有被破坏:
Ndx = UND
),在链接或运行时从另一个目标文件提供所以你extern "C"
在打电话时都需要两个:
g++
我期望生成的无符号符号gcc
g++
生成gcc
要使用的未编码符号在extern C中不起作用的事情
很明显,任何需要名称修改的C++特性都无法在内部工作extern C
:
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
Run Code Online (Sandbox Code Playgroud)
来自C++示例的最小可运行C
为了完整性和新手,请参阅:如何在C++项目中使用C源文件?
从C++调用C非常简单:每个C函数只有一个可能的非破坏符号,因此不需要额外的工作.
main.cpp中
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
Run Code Online (Sandbox Code Playgroud)
CH
#include "c.h"
int f(void) { return 1; }
Run Code Online (Sandbox Code Playgroud)
CC
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
Run Code Online (Sandbox Code Playgroud)
跑:
main.cpp:6: undefined reference to `f()'
Run Code Online (Sandbox Code Playgroud)
没有extern "C"
链接失败:
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
因为g++
期望找到一个受损的f
,gcc
没有产生.
来自C示例的最小可运行C++
从中调用C++有点困难:我们必须手动创建我们想要公开的每个函数的非破坏版本.
这里我们说明如何将C++函数重载暴露给C.
main.c中
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
Run Code Online (Sandbox Code Playgroud)
cpp.h
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}
Run Code Online (Sandbox Code Playgroud)
cpp.cpp
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
Run Code Online (Sandbox Code Playgroud)
跑:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
Run Code Online (Sandbox Code Playgroud)
没有extern "C"
它失败了:
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Run Code Online (Sandbox Code Playgroud)
因为g++
生成了gcc
无法找到的错位符号.
在Ubuntu 18.04中测试过.
sud*_*03r 199
在每个C++程序中,所有非静态函数都在二进制文件中表示为符号.这些符号是特殊文本字符串,用于唯一标识程序中的函数.
在C中,符号名称与函数名称相同.这是可能的,因为在C中没有两个非静态函数可以具有相同的名称.
因为C++允许重载并且具有C不具备的许多功能 - 比如类,成员函数,异常规范 - 所以不可能简单地使用函数名作为符号名.为了解决这个问题,C++使用了所谓的名称修改,它将函数名称和所有必要信息(如参数的数量和大小)转换为仅由编译器和链接器处理的奇怪字符串.
因此,如果您将函数指定为extern C,则编译器不会对其执行名称修改,并且可以使用其符号名称作为函数名称直接访问它.
这在使用dlsym()
和dlopen()
调用此类函数时非常方便.
tfm*_*gue 44
大多数编程语言都不是在现有编程语言的基础上构建的.C++是在C语言之上构建的,而且它是一种基于过程编程语言构建的面向对象编程语言,因此有一些C++关键字extern "C"
可以向后兼容C语言.
我们来看下面的例子:
#include <stdio.h>
// Two functions are defined with the same name
// but have different parameters
void printMe(int a) {
printf("int: %i\n", a);
}
void printMe(char a) {
printf("char: %c\n", a);
}
int main() {
printMe("a");
printMe(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
AC编译器不会编译上面的例子,因为相同的函数printMe
定义了两次(即使它们具有不同的参数int a
vs char a
).
gcc -o printMe printMe.c && ./printMe;
1错误.PrintMe定义不止一次.
C++编译器将编译上面的示例.它并不关心printMe
定义两次.
g ++ -o printMe printMe.c && ./printMe;
这是因为C++编译器根据其参数隐式重命名(mangles)函数.在C中,不支持此功能.但是,当C++是基于C构建的时,该语言被设计为面向对象的,并且需要支持使用相同名称的方法(函数)创建不同类的能力,并且基于不同的方法覆盖方法(方法重写)参数.
但是,假设我们有一个名为"parent.c" extern "C"
的遗留C文件,它来自其他遗留C文件的函数名称,"parent.h","child.h"等.如果运行旧版"parent.c"文件通过C++编译器,函数名称将被破坏,它们将不再匹配"parent.h","child.h"等中指定的函数名称 - 因此这些外部文件中的函数名称也需要被破坏了.在复杂的C程序中管理函数名称,那些具有大量依赖性的程序可能导致代码损坏; 因此提供一个可以告诉C++编译器不会破坏函数名的关键字可能会很方便.
该include
关键字告诉C++编译器不要破坏(重命名)函数名称.用法示例:extern "C"
San*_*ens 25
通过仅包装extern"C",不能使任何C-header与C++兼容.当C-header中的标识符与C++关键字冲突时,C++编译器会抱怨这一点.
例如,我在g ++中看到以下代码失败:
extern "C" {
struct method {
int virtual;
};
}
Run Code Online (Sandbox Code Playgroud)
Kinda有意义,但在将C代码移植到C++时要记住这一点.
小智 7
我使用'extern"C"'之前为dll(动态链接库)文件制作等main()函数"可导出",以便稍后可以在dll的另一个可执行文件中使用它.也许我以前使用它的例子很有用.
DLL
#include <string.h>
#include <windows.h>
using namespace std;
#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}
Run Code Online (Sandbox Code Playgroud)
可执行程序
#include <string.h>
#include <windows.h>
using namespace std;
typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder
int main()
{
char winDir[MAX_PATH];//will hold path of above dll
GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
strcat(winDir,"\\exmple.dll");//concentrate dll name with path
HINSTANCE DLL = LoadLibrary(winDir);//load example dll
if(DLL==NULL)
{
FreeLibrary((HMODULE)DLL);//if load fails exit
return 0;
}
mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
//defined variable is used to assign a function from dll
//GetProcAddress is used to locate function with pre defined extern name "DLL"
//and matcing function name
if(mainDLLFunc==NULL)
{
FreeLibrary((HMODULE)DLL);//if it fails exit
return 0;
}
mainDLLFunc();//run exported function
FreeLibrary((HMODULE)DLL);
}
Run Code Online (Sandbox Code Playgroud)
extern "C"
是一个链接规范,用于调用Cpp源文件中的C函数.我们可以调用C函数,编写变量,并包含头文件.函数在extern实体中声明,并在外部定义.语法是
类型1:
extern "language" function-prototype
Run Code Online (Sandbox Code Playgroud)
类型2:
extern "language"
{
function-prototype
};
Run Code Online (Sandbox Code Playgroud)
例如:
#include<iostream>
using namespace std;
extern "C"
{
#include<stdio.h> // Include C Header
int n; // Declare a Variable
void func(int,int); // Declare a function (function prototype)
}
int main()
{
func(int a, int b); // Calling function . . .
return 0;
}
// Function definition . . .
void func(int m, int n)
{
//
//
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
724660 次 |
最近记录: |