当前位置: 首页 > news >正文

extern “C“ _declspec(dllexport)导出函数

文章目录

  • 前言
  • 一、整体代码解释
  • 二、关键修饰符详解
    • Ⅰ、`extern "C"`:按C语言规则编译链接,解决名字修饰问题
      • 1. 核心作用
      • 2. 为什么需要它?—— 破解C++的“名字修饰”(Name Mangling)
      • 3. 使用注意事项
    • Ⅱ、 `_declspec(dllexport)`:标记函数为DLL导出符号
      • 1. 核心作用
      • 2. 为什么需要它?—— 明确DLL的公开接口
      • 3. 使用注意事项
  • 三、两者的配合效果
  • 四、扩展知识
  • 五、能否省略
    • Ⅰ、`_declspec(dllexport)`:告诉编译器「这个函数要导出到DLL」
      • 核心作用
      • 能否省略?
      • 例外情况:用.def文件替代
    • Ⅱ、`extern "C"`:强制用C语言的规则编译函数
      • 核心作用
      • 能否省略?
      • 注意点
    • Ⅲ、两者的关系与组合逻辑
    • Ⅳ、总结
  • 总结


前言

前面文章介绍了Windows环境下制作库文件导出函数的步骤,现在对其书写格式进行剖析说明记录。


extern "C"  _declspec(dllexport) int add(int a, int b)
{return a + b;
}

一、整体代码解释

这段代码的核心作用是:在C++环境中定义一个可被外部模块(如其他DLL、EXE)调用的加法函数,并将其导出为动态链接库(DLL)的公开接口

  • 函数 add(int a, int b):接收两个整型参数,返回它们的和,是核心功能实现。
  • extern "C"_declspec(dllexport):是实现“函数导出”和“跨语言/编译器兼容”的关键修饰符,二者配合确保外部模块能正确找到并调用该函数。

二、关键修饰符详解

Ⅰ、extern "C":按C语言规则编译链接,解决名字修饰问题

1. 核心作用

告诉C++编译器:后续的函数/代码块,按照C语言的编译和链接规则处理,而非C++规则

2. 为什么需要它?—— 破解C++的“名字修饰”(Name Mangling)

C++支持函数重载(比如 add(int, int)add(double, double) 可以共存),为了区分不同的重载函数,编译器会对函数名进行“名字修饰”:

  • 例如C++中 add(int, int) 可能被MSVC编译器修饰为 ?add@@YAHHH@Z,GCC则可能修饰为 _Z3addii
  • 而C语言不支持重载,不会对函数名做任何修饰,add(int, int) 的编译后名字就是 add

如果没有 extern "C",DLL导出的函数名会是C++修饰后的名字,导致:

  • 其他C语言编写的程序(或不支持C++名字修饰的语言)调用时,找不到名为 add 的函数(实际导出的是修饰名)。
  • 跨编译器调用失败(MSVC和GCC的修饰规则不同)。

extern "C" 就是强制编译器禁用名字修饰,让函数名保持C语言风格的原始名称(如 add),确保跨语言(C/C++)、跨编译器兼容

3. 使用注意事项

  • 仅对函数、全局变量有效,不能修饰类成员函数(C语言无“类”概念,成员函数依赖this指针,无法按C规则编译)。
  • extern "C" 修饰的函数不能重载(C语言不支持重载,若重载会导致名字冲突)。
  • 作用域:可修饰单个函数,也可修饰代码块(多个函数):
    extern "C" {int add(int a, int b);int sub(int a, int b); // 多个函数按C规则编译
    }
    

Ⅱ、 _declspec(dllexport):标记函数为DLL导出符号

1. 核心作用

这是微软MSVC编译器特有的指令,告诉编译器:将该函数(或变量、类)标记为“DLL导出符号”,并写入DLL的“导出表”(Export Table)

  • DLL的“导出表”是一个清单,记录了DLL对外公开的可调用函数/变量的名称、地址。
  • 外部模块(如其他EXE)加载DLL时,会通过导出表查找需要调用的函数,若函数未被标记为 dllexport,则不会出现在导出表中,外部无法调用。

2. 为什么需要它?—— 明确DLL的公开接口

DLL中的函数默认是“内部私有”的,仅能被DLL自身代码调用。_declspec(dllexport) 相当于给函数加了“公开标识”,告诉编译器:“这个函数是给外部用的,要对外暴露”。

3. 使用注意事项

  • 平台依赖性:仅在 MSVC编译器 有效(GCC/Clang用 __attribute__((visibility("default"))) 实现类似功能)。
  • 可导出的对象:函数、全局变量、整个类(修饰类时,类的所有成员函数都会被导出,但不推荐导出整个类,冗余且易引发兼容性问题)。
  • 对应的导入指令:外部模块调用DLL导出函数时,需用 _declspec(dllimport) 声明(告诉编译器“该函数来自外部DLL”),示例:
    // 其他模块调用时的声明
    extern "C" _declspec(dllimport) int add(int a, int b);
    
  • 替代方案:除了 _declspec(dllexport),也可通过 .def 文件(模块定义文件)指定导出函数,但 dllexport 更简洁(直接在代码中标记,无需额外文件)。

三、两者的配合效果

extern "C" + _declspec(dllexport) 是Windows平台DLL导出函数的“黄金组合”,最终实现:

  1. 函数按C语言规则编译,名字保持原始 add(无修饰),确保跨语言/编译器兼容。
  2. 函数被写入DLL导出表,成为公开接口,外部模块可通过 add 名称找到并调用。

四、扩展知识

  1. 若仅用 _declspec(dllexport) 不用 extern "C":导出的函数名是C++修饰后的名字(如 ?add@@YAHHH@Z),外部模块需按修饰名调用,极不友好。
  2. 若仅用 extern "C" 不用 _declspec(dllexport):函数按C规则编译,但未被导出到DLL导出表,外部模块无法调用。
  3. 跨平台兼容性:_declspec(dllexport) 是MSVC特有,若需编写跨Windows/Linux的DLL,可通过条件编译适配:
    #ifdef _WIN32
    #define DLL_EXPORT _declspec(dllexport)
    #else
    #define DLL_EXPORT __attribute__((visibility("default")))
    #endifextern "C" DLL_EXPORT int add(int a, int b) {return a + b;
    }
    

五、能否省略

要理解extern "C" _declspec(dllexport)这两个关键字的作用,需结合 Windows DLL的导出机制C/C++的编译差异 来分析,下面逐一拆解核心作用、能否省略及后果:

Ⅰ、_declspec(dllexport):告诉编译器「这个函数要导出到DLL」

核心作用

Windows的DLL(动态链接库)本质是一个包含可复用代码的二进制文件,但并非内部所有函数都会对外暴露——只有被标记为「导出函数」的符号,才会被写入DLL的「导出表」(Export Table),外部程序(如exe、其他DLL)加载该DLL时,才能通过函数名或序号找到并调用它。

_declspec(dllexport)Microsoft编译器(MSVC)特有的语法,作用是直接在代码中标记函数为「导出符号」,编译器编译时会自动将其加入DLL的导出表,无需额外配置。

能否省略?

默认情况下(无.def文件),不能省略!
如果省略,该函数会被当作DLL的「内部函数」(仅DLL自身可调用),不会写入导出表,外部程序调用时会报「无法解析的外部符号」(Link Error)。

例外情况:用.def文件替代

除了代码中加 _declspec(dllexport),也可以通过「模块定义文件(.def)」来指定导出函数。例如:

; 文件名:mydll.def
LIBRARY mydll  ; DLL名称
EXPORTS        ; 导出函数列表add        ; 要导出的函数名

此时代码中可省略 _declspec(dllexport),编译器会根据.def文件的配置导出函数。但实际开发中,直接在代码中加关键字更简洁,def文件多用于复杂场景(如指定函数导出序号、重命名导出函数)。

Ⅱ、extern "C":强制用C语言的规则编译函数

核心作用

C++支持函数重载(同名函数不同参数),为了区分重载函数,编译器会对函数名进行「名字修饰(Name Mangling)」——在编译后的目标文件中,函数名会被附加参数类型、返回值类型等信息(例如 int add(int a, int b) 会被MSVC修饰为 ?add@@YAHHH@Z)。

而C语言不支持重载,名字修饰规则极简单(通常直接保留函数名,如 add)。extern "C" 的作用是告诉C++编译器:「这个函数用C语言的规则编译,不要进行C++的名字修饰,也遵循C的调用约定(默认__cdecl)」。

具体带来两个关键效果:

  1. 函数名不被C++修饰:导出到DLL的函数名是原始名称(如 add),而非修饰后的复杂名称,外部程序(尤其是C语言、C#、Python等非C++程序)能直接通过原始函数名调用;
  2. 统一调用约定:C和C++的默认调用约定(__cdecl)一致,避免因调用约定不匹配导致的栈异常。

能否省略?

语法上可以省略,但实际开发中几乎必须保留(除非明确仅C++程序调用)!
省略后会导致两个严重问题:

  1. 函数名被C++修饰:DLL导出表中函数名变成修饰后的形式(如 ?add@@YAHHH@Z),外部程序(如C程序、Python)不知道修饰规则,无法通过原始名 add 找到函数;
  2. 跨语言/编译器兼容性失效:如果调用方是C语言、C#、Java、Python等非C++语言,这些语言仅支持C语言的名字修饰和调用约定,省略 extern "C" 后会完全无法调用;即使调用方是C++,若编译器不同(如MSVC和GCC),名字修饰规则也不统一,会导致链接失败。

注意点

  • extern "C" 仅对「函数、全局变量」有效,不能修饰C++类成员函数(类成员函数依赖C++的this指针和名字修饰,无法用C规则编译);
  • 作用范围:可修饰单个函数,也可包裹多个函数(如 extern "C" { int add(...); int sub(...); })。

Ⅲ、两者的关系与组合逻辑

代码中 extern "C"_declspec(dllexport) 的顺序不影响效果(如 extern "C" __declspec(dllexport) int add(...)__declspec(dllexport) extern "C" int add(...) 均可),核心是「分工明确」:

  • extern "C":解决「编译后函数名是什么」的问题(保证外部能找到);
  • _declspec(dllexport):解决「函数是否被导出到DLL」的问题(保证外部能调用)。

Ⅳ、总结

关键字核心作用能否省略(默认情况)省略后果
_declspec(dllexport)标记函数为DLL导出符号,写入导出表不能(无.def时)函数不导出,外部程序调用失败(链接错误)
extern "C"用C规则编译,避免C++名字修饰,保证兼容性可以(语法允许)函数名被修饰,跨语言/编译器调用失败

实际开发建议:两者都不能省!

  • 若省略 _declspec(dllexport):函数没导出,等于白写;
  • 若省略 extern "C":仅同编译器、同设置的C++程序能调用,兼容性极差(几乎没人这么用)。

最终的「标准写法」就是你给出的形式,兼顾了「导出有效性」和「跨语言兼容性」。


总结

记录型博客,c++工程这一块遇到了很多问题,逐个记录,尽量给串起来。

http://www.dtcms.com/a/619042.html

相关文章:

  • 堡垒机的概念
  • 工信部网站信息查询做电影解析网站
  • 自贡市网站建设网站建设设计价格
  • 化学式字符串解析:一道考验递归功底的经典题
  • 免费推广网站平台成都定制网站建
  • 【Java 微服务中间件】RabbitMQ 全方位解析:同步异步对比、SpringAMQT基础入门、实战、交换机类型及消息处理详解
  • 做网站的收益lnmp wordpress 伪静态
  • 华为HuaweiCloudStack(一)介绍与架构
  • 招商网站建设定做如何给wordpress增加表单
  • 网站维护项目织梦网站安装播放视频插件下载
  • mysql锁机制入门笔记(备份)
  • Snakemake 从入门到实战:生信自动化工作流搭建指南
  • 深入理解CSS布局:从格子布局到响应式栅格系统
  • 做一个电商网站网站源代码下载软件
  • 网站怎么做网上报名河北网站建设方案
  • 珲春建设局网站备案用网站建设方案书
  • 南京小程序开发网站建设网页设计一页多少钱
  • 做投标的网站南平建设集集团网站
  • 以投资思维做网站h5响应式网站开发
  • 做旅游攻略去什么网站密云网站建设公司
  • Linux 中的 CPU。文章 1. 利用率
  • 自然语言处理实战——基于混合专家模型(MoE)的文本生成
  • ps做网站浏览器预览网站备案号显示红色
  • 暗黑模式【闪白】解决方案
  • Spring Boot + Vue 实现一个在线商城(商品展示、购物车、订单)!从零到一完整项目
  • h5可以制作公司网站吗网站用什么框架做
  • AlmaLinux9.6 部署 MariaDB10.11 和 Zabbix7.0 完整教程
  • 东莞市手机网站建设怎么样自己如何做微信小程序
  • 怎么提升网站收录编程培训班学费一般多少钱
  • Git 在团队中的最佳实践--如何正确使用Git Flow