30.externC的用法

30.externC的用法

为了能够正确地在C++代码中调用C语言的代码:在程序中加上extern "C"后,相当于告诉编译器这部分代码是C语言写的,因此要按照C语言进行编译,而不是C++;

哪些情况下使用extern "C":

(1)C++代码中调用C语言代码;

(2)在C++中的头文件中使用;

(3)在多个人协同开发时,可能有人擅长C语言,而有人擅长C++;

总结出如下形式:

(1)C++调用C函数:

当你有一个 C 函数在一个库或源文件中,并希望在 C++ 代码中调用它时,你需要确保 C++ 编译器知道这个函数具有 C 链接约定,而不是 C++ 链接约定。为了实现这一点,你会使用 extern "C"

下面是一个完整的示例来展示如何做到这一点:

C 代码 (c_functions.c):

#include <stdio.h>

void c_function() {
    printf("This is a C function.\n");
}

C 头文件 (c_functions.h):

#ifndef C_FUNCTIONS_H
#define C_FUNCTIONS_H

// 用于 C++ 中
#ifdef __cplusplus
extern "C" {
#endif

void c_function();

#ifdef __cplusplus
}
#endif

#endif // C_FUNCTIONS_H

好的,这是一个典型的C语言的头文件,用于保证C函数可以被C++代码调用。我会逐行为您解释。

#ifndef C_FUNCTIONS_H

这是一个预处理指令,检查C_FUNCTIONS_H是否已定义。如果尚未定义,代码将继续执行,直到遇到与此匹配的#endif。这是所谓的“包含保护”或“头文件保护”,用于确保头文件在一个编译单位中只被包含一次。这可以避免因重复包含而引起的重复定义。

#define C_FUNCTIONS_H

这里定义了C_FUNCTIONS_H,因此下次再包含这个头文件时,#ifndef将检查到它已经定义,从而防止头文件的内容被重复包含。

// 用于 C++ 中

这是一个注释,表示以下的代码是为C++设计的。

#ifdef __cplusplus

这是一个预处理指令,检查是否定义了__cplusplus宏。__cplusplus是一个内置的宏,在C++代码中被自动定义。这样,只有在C++中编译时,它下面的代码才会被包括进来。

extern "C" {

这告诉C++编译器以下的代码是按C语言的方式进行链接。C++因为其函数重载的特性,会对函数名进行“名称修饰”(name mangling),而C没有这个特性。当我们在C++中调用C函数时,需要告诉C++编译器不要对这些函数名进行名称修饰。

void c_function();

这是一个C函数的声明。

#ifdef __cplusplus
}
#endif

这些代码结束了extern "C"块。如果在C++环境中,这段代码将关闭extern "C"声明,意味着之后的代码不再需要按C语言的方式链接。

#endif // C_FUNCTIONS_H

这是与最初的#ifndef C_FUNCTIONS_H相匹配的#endif,标记着“包含保护”的结束。

总结:这个头文件的主要目的是确保C函数可以在C++中被正确调用,同时也避免头文件被重复包含。

C++ 代码 (main.cpp):

#include <iostream>
#include "c_functions.h"

int main() 
{
    std::cout << "Calling from C++ code." << std::endl;
    c_function();
    return 0;
}

当你要编译这些代码时,你需要确保同时编译 C 和 C++ 源文件。例如,使用 gccg++

gcc -c c_functions.c
g++ main.cpp c_functions.o -o my_program

在这个例子中,我们在 C 头文件中使用 extern "C" 来告诉 C++ 编译器 c_function 使用的是 C 链接约定。这确保了当我们在 C++ 代码中调用 c_function 时,链接器能够找到正确的函数,因为它的名字没有经过 C++ 的名字改编(name mangling)。

(2)在C++中的头文件中使用;

①在CppClass.h中,您应该仅声明C函数。包含它的C头文件可能更加合适,而不是直接在C++头文件中声明。

②我们不应该在C的源文件中包含C++的头文件。

让我给您一个修正后的例子。

首先是C函数的声明和定义:

C语言的头文件 (c_functions.h):

// c_functions.h
#ifndef C_FUNCTIONS_H
#define C_FUNCTIONS_H

void my_c_function();

#endif // C_FUNCTIONS_H

C语言的实现文件 (c_functions.c):

// c_functions.c
#include "c_functions.h"
#include <stdio.h>

void my_c_function() {
    // 实现部分,例如打印一个消息
    printf("This is a function written in C.\n");
}

然后是C++类的声明和定义:

C++的头文件 (CppClass.h):

// CppClass.h
#ifndef CPPCLASS_H
#define CPPCLASS_H

#ifdef __cplusplus
extern "C" {
#include "c_functions.h"
}
#endif

class CppClass {
public:
    void callCFunction();
};

#endif // CPPCLASS_H

C++的实现文件 (CppClass.cpp):

// CppClass.cpp
#include "CppClass.h"
#include <iostream>

void CppClass::callCFunction() {
    std::cout << "Calling a C function from C++ method:" << std::endl;
    my_c_function();
}

最后是主文件:

主C++源文件 (main.cpp):

// main.cpp
#include <iostream>
#include "CppClass.h"

int main() {
    CppClass obj;
    obj.callCFunction(); // 在C++类方法中调用C函数
    return 0;
}

现在,C++的头文件CppClass.h使用extern "C"将C的头文件c_functions.h包围起来,这确保了在C++代码中正确地调用C函数。

(3)一些其他用法

①变量的调用

在extern1.cpp中定义一个全局变量:

int a = 12;

在externMain.cpp中:

#include <iostream>
using namespace std;
 
int a;
 
int main()
{
	cout << a << endl;
 
	system("pause");
	return 0;
}

这样编译就会出现错误:

这时需要在externMain.cpp中,在int a前面添加关键字extern

extern int a;

这时便能成功打印出来变量a的值了,注意变量前面的类型不能省略,否则编译器报错:缺少类型说明符

②函数的调用

在extern1.cpp中,定义一个函数:

#include <iostream>
using namespace std;
 
void fun()
{
	cout << "helloworld" << endl;
}

在externMain.cpp中调用该函数:

#include <iostream>
using namespace std;
 
int main()
{
	fun();
 
	system("pause");
	return 0;
}

如果这样直接编译的话,会报错:

需要在函数使用前进行声明:

#include <iostream>
using namespace std;
 
void fun();
 
int main()
{
	fun();
 
	system("pause");
	return 0;
}

如果我们在函数声明的地方加上extern关键字,程序也是可以运行的:

extern void fun();

对于外部函数,extern可以省略,但对于外部变量,extern就不能被省略

posted @ 2023-07-03 21:23  CodeMagicianT  阅读(39)  评论(0编辑  收藏  举报