IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> extern 和 extern “C“ -> 正文阅读

[C++知识库]extern 和 extern “C“

extern

extern 是用来进行外部声明的。

谨记:声明可以多次,但是定义只能有一次。

?函数的声明extern关键字是可有可无的,因为函数本身不加修饰的话就是extern的。?

  • 当我们需要使用在其它文件中定义的全局变量或全局函数时,需要先用extern进行外部声明,然后才能在当前文件中使用该全局变量或全局函数;
//util.cpp
#include <iostream>
using namespace std;
int num = 20;	//全局变量
void fn(){	//全局函数
    cout << num << endl;
}
//index.cpp
extern int num;
extern void fn();
void main(){
    fn();
    num = 100;
    fn();
}

extern "C"?

????????extern "C"?是C++特有的指令(C无法使用该指令),目的在于支持C++与C混合编程。

????????extern "C"?告诉C++编译器用C规则编译指定的代码(除函数重载外,extern “C”不影响C++其他特性)。

????????因为C和C++的编译规则不一样,主要区别体现在编译期间生成函数符号的规则不一致。

????????由于C++需要支持重载,单纯的函数名无法区分出具体的函数,所以在编译阶段就需要将形参列表作为附加项增加到函数符号中。如以下代码

void Function(int a, int b)
{
    printf("Hello!!! a = %d, b = %d\n", a, b);
}

C和C++对应的的汇编码如下

  • C汇编结果
...
Function:
.LFB11:
    .cfi_startproc
    movl    %esi, %edx
    xorl    %eax, %eax
    movl    %edi, %esi
    movl    $.LC0, %edi
    jmp    printf
    .cfi_endproc
...
  • C++汇编结果
...
_Z8Functionii:
.LFB12:
    .cfi_startproc
    movl    %esi, %edx
    xorl    %eax, %eax
    movl    %edi, %esi
    movl    $.LC0, %edi
    jmp    printf
    .cfi_endproc
...

容易发现,两段代码的区别仅在于函数 Function(int a, int b) 编译后对应的符号不同

C:Function

C++:_Z8Functionii

C++编出来的函数符号明显比C的多出了一些信息(如ii),这里多出来的后缀信息就是形参列表的参数类型信息。

extern "C"例子1

/* MyFunction.c */
void Function(int a, int b)
{
    printf("Hello!!! a = %d, b = %d\n", a, b);
}
 
 
/* main,cpp */
extern void Function(int a, int b);
 
int main()
{
    Function(1, 2);
}

以上代码,C提供写了一个函数,用C++代码调用该函数,看起来没什么问题,但是编译的时候...

找不到对Function(int, int)的定义

来看看两个文件的汇编结果。

.file    "MyFunction.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "Hello!!! a = %d, b = %d\n"
    .text
    .p2align 4,,15
    .globl    Function
    .type    Function, @function
Function:
.LFB11:
    .cfi_startproc
    movl    %esi, %edx
    xorl    %eax, %eax
    movl    %edi, %esi
    movl    $.LC0, %edi
    jmp    printf
    .cfi_endproc
.LFE11:
    .size    Function, .-Function
    .ident    "GCC: (GNU) 6.4.1 20170727 (Red Hat 6.4.1-1)"
    .section    .note.GNU-stack,"",@progbits
.file    "main.cpp"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl    main
    .type    main, @function
main:
.LFB0:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $2, %esi
    movl    $1, %edi
    call    _Z8Functionii
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE0:
    .size    main, .-main
    .ident    "GCC: (GNU) 6.4.1 20170727 (Red Hat 6.4.1-1)"
    .section    .note.GNU-stack,"",@progbits

可以看到,MyFunction.s(源文件为.c文件)中定义的是Function,而main.s(源文件为.cpp文件)中调用的是_Z8Functionii,函数名不一样,所以连接的时候找不到函数实现。到这里我们知道C和C++编译期间后得到的函数符号不同,所以C++代码和C代码不能互相调用。

要想实现C、C++混合编程该怎么办呢?用extern "C"!

所以,extern “C”的作用就是告诉C++编译器,将指定的函数用C规则编译(注意,除了函数重载外,extern “C”不影响C++的其他特性),然后后面的事情就顺理成章了。

extern "C"例子2

在一个dll动态链接库中,头文件如下:声明 fnDll1();

#ifdef DLL1_EXPORTS
#define DLL1_API __declspec(dllexport)
#else
#define DLL1_API __declspec(dllimport)
#endif

DLL1_API int fnDll1(); // extern DLL1_API int fnDll1() 也可以

对应cpp中定义 fnDll1() 函数

int fnDll1() {
    return 100;
}

在控制台项目中,main.cpp?通过 LoadLibrary 调用.dll

void dllCall() {
	typedef int(*FnPoint)();

	HMODULE HD = LoadLibrary(L"Dll1.dll");
	if (!HD) {
		cout << "HD is null." << endl;
		return;
	}
	FnPoint FD = (FnPoint)GetProcAddress(HD, "fnDll1");
	if (!FD) {
		cout << "FD is null." << endl;
		return;
	}
	cout << FD() << endl;
}

此时运行程序,结果如下

dll文件引入成功了,但是fnDll1函数却没有拿到。造成这种结果是因为,dll中的fnDll1通过C++进行编译,导致最终的名字并不是原来的了。

解决方法:

方法1:使用extern "C",修改dll头文件。这样fnDll1 仍然是 fnDll1

#ifdef __cplusplus  // 如果是C++就渲染`extern "C"{`;
extern "C" {
#endif // __cplusplus

	DLL1_API int fnDll1();

#ifdef __cplusplus
}
#endif // __cplusplus

或

extern "C" DLL1_API int fnDll1();

方法2:使用.def导出,创建Source.def,并在配置中设置。最终名称就是.def文件中的名称。

LIBRARY "Dll1"

EXPORTS
fnDll1     @1

?方法3(不推荐,仅供参考):使用fnDll1被C++编译之后的名字

在命令行中cd进入.dll文件的目录下。执行:

dumpbin -exports Dll1.dll

将 main.cpp 修改:

FnPoint FD = (FnPoint)GetProcAddress(HD, "?fnDll1@@YAHXZ");

extern “C“的作用及理解_米碎师兄的博客-CSDN博客_externc

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:06:35  更:2022-11-05 00:09:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 5:05:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码