将外部“C”放入C++代码中具体做什么?
例如:
extern "C" {
void foo();
}
将外部“C”放入C++代码中具体做什么?
例如:
extern "C" {
void foo();
}
当前回答
extern“C”是一个链接规范,用于调用Cpp源文件中的C函数。我们可以调用C函数、编写变量和包含头。函数在外部实体中声明,并在外部定义。语法为
类型1:
extern "language" function-prototype
类型2:
extern "language"
{
function-prototype
};
eg:
#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)
{
//
//
}
其他回答
它以这样一种方式更改函数的链接,即该函数可以从C调用。实际上,这意味着函数名不会被破坏。
我之前对dll(动态链接库)文件使用了“extern”C“”,以使etc.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);
}
EXE
#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);
}
这个答案是针对那些不耐烦/有最后期限的人的,下面只是部分/简单的解释:
在C++中,您可以通过重载在类中使用相同的名称(例如,由于它们都是相同的名称,因此无法从dll等导出)。解决这些问题的方法是将它们转换为不同的字符串(称为符号),符号说明了函数的名称,也说明了参数,因此即使这些函数具有相同的名称也可以唯一标识(也称为名称损坏)在C中,您没有重载,函数名是唯一的(因此,不需要单独的字符串来唯一标识函数名,因此符号是函数名本身)
所以在C++中,名称mangling唯一标识每个函数在C语言中,即使没有名字,也可以唯一地标识每个函数
要更改C++的行为,即指定不应对特定函数进行名称篡改,可以在函数名称之前使用外部“C”,无论出于何种原因,例如从dll导出具有特定名称的函数,供其客户端使用。
阅读其他答案,了解更详细/更正确的答案。
它通知C++编译器在链接时以C样式查找这些函数的名称,因为在链接阶段,用C和C++编译的函数的名称不同。
分解一个g++生成的二进制文件,看看发生了什么
主.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
编译并反汇编生成的ELF输出:
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
输出包含:
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
理解
我们看到:
ef和eg存储在与代码中同名的符号中其他符号都被弄乱了。让我们解开它们:$c++过滤器_Z1fvf()$c++过滤器_Z1hvh()$c++过滤器_Z1gvg()
结论:以下两种符号类型均未受损:
定义已声明但未定义(Ndx=UND),将在链接或运行时从另一个对象文件提供
因此,在调用以下两个函数时,都需要外部“C”:
C++中的C:告诉g++期望gcc生成的未成文法的符号来自C的C++:告诉g++生成未成文法的符号供gcc使用
在extern C中不起作用的东西
很明显,任何需要更改名称的C++特性都不会在外部C中工作:
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) { }
}
C++示例中的最小可运行C
为了完整性和新手,请参阅:如何在C++项目中使用C源文件?
从C++调用C非常简单:每个C函数只有一个可能的非损坏符号,因此不需要额外的工作。
主.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
c.h
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++
* because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
c.c
#include "c.h"
int f(void) { return 1; }
Run:
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
如果没有外部“C”,链接将失败:
main.cpp:6: undefined reference to `f()'
因为g++希望找到一个损坏的f,而gcc并没有产生。
GitHub上的示例。
C示例中的最小可运行C++
从C调用C++有点困难:我们必须手动创建要公开的每个函数的未损坏版本。
这里我们说明如何将C++函数重载暴露给C。
主.c
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
每小时
#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
cpp.cpp
#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:
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
如果没有外部“C”,则失败:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
因为g++生成了gcc找不到的损坏符号。
GitHub上的示例。
当我包含c++中的c头时,外部“c”在哪里?
C++版本的C头文件(如csdio)可能依赖于#pragma GCC system_headerhttps://gcc.gnu.org/onlinedocs/cpp/System-Headers.html提到:“在一些目标上,例如RS/6000AIX,当编译为C++时,GCC隐式地用‘extern‘C‘块包围所有系统头”,但我没有完全确认。像/usr/include/unistd.h这样的POSIX头包含在:我需要一个外部“C”块来包含标准的POSIX C头吗?通过__BEGIN_DECLS,在Ubuntu 20.04上再现__BEGIN_DECLS通过#include<features.h>包含。
在Ubuntu 18.04中测试。