__cplusplus和extern “C“

news2025/1/15 23:42:30

文章目录

  • __cplusplus是什么
  • extern "C"
  • 使用场景的示例
  • 通过MinGW编译及查看下目标文件中的符号
    • 用gcc编译器添加 -c选项 使my_handle.c文件编译后生成my_handle.o文件,这里的 -o是 output的意思
    • nm命令 是GCC编译集合下最常用的查看目标文件中的符号的命令 -A选择可以展示目标文件名
    • 同理对my_handle_client.cpp用g++编译器编译
    • 在my_handle_client.cpp文件使用#include "my_handle.h",而不通过extern "C"{}对其进行包裹。
    • 符号类型的说明

__cplusplus是什么

  1. 指定gcc编译 .c文件,__cplusplus没有定义,编译器按照c编译代码
  2. 指定gcc编译 .cpp文件,__cplusplus有定义,编译器按照c++编译代码
  3. 指定g++编译 .c文件,__cplusplus有定义,编译器按照c++编译代码
  4. 指定g++编译 .cpp文件,__cplusplus有定义,编译器按照c++编译代码
    上面这四条都是正确的。
    __cplusplus是gcc编译器在编译.cpp文件或g++编译器在编译.c/.cpp文件时需要加入的宏定义;这个宏定义标志着编译器会把代码按C++的语法来解释。注意MSVC编译器不会加入这个预定义宏。只有类unix环境的GNU编译组件中才有。
#ifdef __cplusplus
extern "C"{ //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
#endif
/*.................................
 * do something here
 *.................................
 */
#ifdef __cplusplus
}
#endif

代码说明:#ifdef __cplusplus 如果当前文件中已经定义了__cplusplus了,就添加 extern “C”{ ,然后是 #endif,结束条件预编译指令。下面的也是一个判断#ifdef __cplusplus,如果已经定义就 添加},然后,#endif结束条件预编译指令。前面我们说过了 __cplusplus有定义,这个源文件会按照c++编译代码,但是我们又想让一部分指定的代码安装C语言的风格进行编译。所以就得这样处理了。
这里为啥不这样写:如下:

#ifdef __cplusplus             
extern "C"{ //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
/*.................................
 * do something here
 *.................................
 */
}
#endif

是因为如果 __cplusplus 没有定义 那么像上面这样写 do something here这段代码就被屏蔽了,所以 为正常执行 do something here 这里的C代码,我们要把extern “C”{和}用相同条件编译指令分别隔离开。

extern “C”

刚才我们说了添加 extern “C” {…}的作用。下面说下我们为什么要添加 extern “C” {…},下面是百度百科上的一段说明
在这里插入图片描述

使用场景的示例

准备四个文件
1.my_handle.h

#ifndef __MY_HANDLE_H__
#define __MY_HANDLE_H__

typedef unsigned int result_t;
typedef void* my_handle_t;

my_handle_t create_handle(const char* name);
result_t operate_on_handle(my_handle_t handle);
void close_handle(my_handle_t handle);

#endif  /*__MY_HANDLE_H__*/

2.my_handle.c 中引入my_handle.h

#include "my_handle.h"
my_handle_t create_handle(const char* name)
{
	return (my_handle_t)0;//即返回 NULL(空指针)
}
result_t operate_on_handle(my_handle_t handle)
{
	return 0;
}
void close_handle(my_handle_t handle)
{
	
}

3.my_handle_client.h

#pragma once

class my_handle_client
{
public:
	void do_something(const char* name);
};
  1. my_handle_client.cpp 引入 my_handle.h和my_handle_client.h
//extern "C" {
//	#include "my_handle.h"
//}
#include "my_handle.h"
#include "my_handle_client.h"
void my_handle_client::do_something(const char* name)
{
	my_handle_t handle = create_handle(name);
	(void) operate_on_handle(handle);
	close_handle(handle);

}

在这里插入图片描述
这里是在Windows下vs上演示的。下面我们说下报这个错的原因。
我们知道代码的编译流程,从 预编译 到 编译 到 汇编 最终得到 目标文件。window的msvc下生成的是.obj文件。而gcc/g++下生成的是.o文件 都是目标文件。我们写的头文件在预编译阶段 是被拷贝到引入此头文件源文件中的。所以.h文件不参与预编译阶段之后的操作。我们写的每个源文件都是一个编译单元。对应的生成一个同名的.o或.obj的目标文件。
下面说下目标文件里面都有啥,目标文件有函数名及静态存储区中各个变量的类型 及符号。一般情况下符号名和变量名一样。而函数名的符号名 分C/C++下的编译。C编译器下编译的源文件中函数名和符号名一致。而C++编译器下编译源文件 由于C++中存在函数重载机制,编译后目标文件中的符号名和源文件中的函数名相差甚远。
这里我本打算 在window下用msvc下(D:\software\visual_studio_2019\IDE\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x86)提供的dumpbin命令查看下obj文件的内容。但是不支持。经过网上https://stackoverflow.com/questions/11849853/how-to-list-functions-present-in-object-file里在这里插入图片描述
具体怎么设置,这里不清楚。这里说下造成编译报错的原因。原因就在各个目标文件之间进行链接的时候。因为my_handle_client.o文件中调用了三个外部文件中的函数。链接的时候要查找对应的函数实现。my_handle_client.o文件中这三个外部调用函数的符号分别变成了void * __cdecl create_handle(char const *) 和 unsigned int __cdecl operate_on_handle(void *) 和 void __cdecl close_handle(void *),因为把头文件my_handle.h里的代码,按c++编译器来编译了。但是 my_handle.o 是通过MSVC中的C编译器编译来的这三个函数的符号是create_handle,operate_on_handle,close_handle。my_handle_client.o目标文件根据它自己里面的这三个函数符号,去从其他 目标文件里遍历查找对应的函数符号,发现找不到。所以报了无法解析的外部符号的错误提示。所以这里的my_handle_client.cpp中的#include "my_handle.h"要指定通过C编译器来编译,即用extern “C” {}来包起来,这里为啥没加__cplusplus宏定义 是因为 我们的编译环境不是GCC,而是MSVC 。

extern "C" {
	#include "my_handle.h"
}
//#include "my_handle.h"
#include "my_handle_client.h"
void my_handle_client::do_something(const char* name)
{
	my_handle_t handle = create_handle(name);
	(void) operate_on_handle(handle);
	close_handle(handle);

}

代码改成上面这样就可以找到响应的外部符号。

通过MinGW编译及查看下目标文件中的符号

由于我们说过通过msvc下的命令查看不了目标.obj文件的内容。这里由于之前安装过QT,QT Creator中自带MinGW功能。将D:\software\QT\qt5.12.12\Tools\mingw730_32\bin或这个D:\software\QT\qt5.12.12\Tools\mingw730_64\bin添加到系统环境变量下。就可以使用gcc或者g++以及其他MinGW中提供的命令。
需要通过gcc/g++对源进行重新编译。如果 直接用MinGW下的命令操作或查看MSVC环境中编译的.obj文件会识别不了文件。必须重新编译。

用gcc编译器添加 -c选项 使my_handle.c文件编译后生成my_handle.o文件,这里的 -o是 output的意思

D:\vs_project\sln_name_001\sln_pro_001>gcc -c my_handle.c -o my_handle.o

nm命令 是GCC编译集合下最常用的查看目标文件中的符号的命令 -A选择可以展示目标文件名

D:\vs_project\sln_name_001\sln_pro_001>nm -A my_handle.o
my_handle.o:00000000 b .bss
my_handle.o:00000000 d .data
my_handle.o:00000000 r .eh_frame
my_handle.o:00000000 r .rdata$zzz
my_handle.o:00000000 t .text
my_handle.o:00000014 T _close_handle
my_handle.o:00000000 T _create_handle
my_handle.o:0000000a T _operate_on_handle

#这里解释一下:
第一列是目标文件名(my_handle.o),
第二列 是符号的偏移或符号值(00000000),
第三列是符号类型(b )。
第四列是符号名(.bss)

同理对my_handle_client.cpp用g++编译器编译

D:\vs_project\sln_name_001\sln_pro_001>g++ -c my_handle_client.cpp -o my_handle_client.o

D:\vs_project\sln_name_001\sln_pro_001>nm -A my_handle_client.o
my_handle_client.o:00000000 b .bss
my_handle_client.o:00000000 d .data
my_handle_client.o:00000000 r .eh_frame
my_handle_client.o:00000000 r .rdata$zzz
my_handle_client.o:00000000 t .text
my_handle_client.o:00000000 T __ZN16my_handle_client12do_somethingEPKc
my_handle_client.o: U _close_handle
my_handle_client.o: U _create_handle
my_handle_client.o: U _operate_on_handle

#注由于我们在my_handle_client.cpp文件已经对#include "my_handle.h"通过extern “C”{}进行包裹。
#所以这里的通过从头文件my_handle.h中拷贝过来的代码,通过C编译器来编译。得到的函数符号和上面的函数符号一样。

在my_handle_client.cpp文件使用#include “my_handle.h”,而不通过extern “C”{}对其进行包裹。

D:\vs_project\sln_name_001\sln_pro_001>g++ -c my_handle_client.cpp -o my_handle_client.o

D:\vs_project\sln_name_001\sln_pro_001>nm -A my_handle_client.o
my_handle_client.o:00000000 b .bss
my_handle_client.o:00000000 d .data
my_handle_client.o:00000000 r .eh_frame
my_handle_client.o:00000000 r .rdata$zzz
my_handle_client.o:00000000 t .text
my_handle_client.o: U __Z12close_handlePv
my_handle_client.o: U __Z13create_handlePKc
my_handle_client.o: U __Z17operate_on_handlePv
my_handle_client.o:00000000 T __ZN16my_handle_client12do_somethingEPKc

可以看到这里的函数名已经面目全非了,链接的时候 ,就无法根据函数符号找到对应的具体函数实现了。

符号类型的说明

大写的T表示 此符号对应的函数或其他变量名的实现就在当前文件内。大写的U表示对应的实现不在当前文件内,需要在链接阶段,找到具体实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/435652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0601概述-react路由-react

1 SPA与MPA 1.1 简述 单页面应用和多页面应用是两种不同的 Web 应用程序架构。 单页面应用(SPA)是指在一个 HTML 页面中动态加载和渲染所有的应用程序内容,通过前端 JavaScript 操作来实现页面的变化和交互。SPA 不需要每次请求新的 HTML …

牛客刷题错题解析

以下是Video/Audio中会触发的事件的有? load play seeked abort 网址:https://www.nowcoder.com/questionTerminal/fc3b560267fd44e98d02a40a 方法:load() play() pause() 事件:play() playing() pause() seeked() seeking() abor…

Linux移植5.4版本内核:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(5.4版本内核)

Linux移植5.4版本内核:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(5.4版本内核) 文章目录 Linux移植5.4版本内核:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(5.4版本内核)1.出厂源…

浅理解JavaScript数组去重的方法(划重点),当面试官问如何实现数组去重时,你可以这样做...

文章目录 📋前言🎯什么是数组去重,运用场景是什么?🎯常用的数组去重方法🧩使用 Set 对象🧩使用 Object(对象、基于Hash哈希表) 或 Map🧩使用 filter 方法与 i…

概率图降低表示需要的参数指的是什么?(贝叶斯网络) 结构化概率模型

深度学习中经常要对概率密度建模。对于多维度随机变量来说,这有些困难。概率化结构(既图模型)是处理这个问题的手段之一。这引出了两个问题。为什么建模困难?图模型怎样解决了这个困难? 关于这个问题,花书…

图片怎么压缩到200K以内,这3个图片压缩方法,简单有效

你没有遇到过上传图片到网站的时候,图片太大不能上传的情况?还有,许多报名照片要求小于200K,可是照片超过这个大小,应该如何压缩呢?下面我给大家带来3个图片压缩的方法,既能快速压缩图片大小&am…

深度学习技巧应用7-K折交叉验证的实践操作

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用7-K折交叉验证的实践操作。K折交叉验证是一种机器学习中常用的模型验证和选择方式,它可以将数据集分为K个互斥的子集,其中K-1个子集作为训练集,剩下1个子集作为验…

Hive设置元数据支持中文显示

在hive中建外部表时遇见到这样一个问题,就是表字段的中文注释在desc 表结构时看不了,发现原来是Hive的元数据库没有设置支持中文显示 第一步,在元数据库metastore完成初始化后,再次登录MySQL [roothurys24 hurys_table_data]# m…

成功解决:OSError: [E050] Can’t find model ‘en_core_web_sm’.

成功解决OSError: [E050] Can’t find model ‘en_core_web_sm’. 问题描述 在安装spacy包之后,再加载’en_core_web_sm’语言模型时,报出OSError: [E050] Can’t find model ‘en_core_web_sm’. It doesn’t seem to be a Python package or a valid…

【Java】插入排序和希尔排序---图解超详细

目录 插入排序 插入排序的核心图解 希尔排序 希尔排序详细图解 插入排序 插入排序的交换精髓在于 每次随着i的扩大,i走过的路径都是有序的,这和冒泡的思想有异曲同工之处,冒泡是i走一次,数组的最后变成有序的,而插入排序是 插入排序是 i 在前面 j在后面 插入排序的核心图解…

C- 符号

文章目录 符号#ifdef-#endif\接续符转义旋转光标数字倒计时 单引号-双引号逻辑运算符&& ||短路 位运算符异或位运算最好使用定义好的宏左移右移 后置前置复杂表达式 取整0向取整(C中默认取整方式)floor地板取整ceilround 四舍五入 取模取余和取模一样吗? 运算符优先级…

两小时让你全方位的认识文件(一)

想必友友们在生活中经常会使用到各种各样的文件,那么我们是否了解它其中的奥秘呢,今天阿博就带领友友们深入地走入文件🛩️🛩️🛩️ 文章目录 一.为什么使用文件二.什么是文件三.文件的打开和关闭四.文件的顺序读写 一…

网页自动化工具DrissionPage

逛Github时偶然看到的开源项目,DrissionPage是一款新的基于 python 的网页自动化工具。 笔者已测试过,给大家推荐下。 项目地址:https://gitee.com/g1879/DrissionPage 安装测试 安装命令 pip install DrissionPage测试 from Drissio…

unity | 处理string常用的知识(持续更新)

一、转义字符和的用法 1.常规用法 我们现在有一行字,但是我对它的格式之类的有要求 例:天无绝人之路,条条道路通罗马。 我想打成: 天无绝人之路, 条条道路通罗马。 换行前,写法是: string s …

科海思—美国杜笙Tulsimer中国区总代理,制糖脱色树脂A-722

一、产品介绍 具有控制孔径的大孔强碱性Ⅰ型阴特种脱色用离子交换树脂 Tulsimer A-722是一款具有便于颜色和有机物去除的控制孔径的,专门开发的大孔强碱性Ⅰ型阴离子交换树脂。 Tulsimer A-722 (氯型)专门应用于糖浆脱色。 Tulsimer A…

Python学习简记

常用数据类型 整数类型int 二进制以0b开头八进制以0o开头十六进制以0x开头 这里还有一个值得注意的点:python中的整型是“无限长”的,因此它可以表示任何数 浮点数 python中只有float作为浮点数,没有double 主要注意python中对浮点数与Decima…

Spring系统架构与主要概念

Spring系统架构与主要概念 Spring Framework系统架构Core Container 核心容器AOP 层数据层Web层测试层 业务逻辑Spring之前遇到的问题解决方案 Spring核心概念IOC(Inversion of Control)控制反转DI(Dependency Injection)依赖注入…

SRv6实践项目(四):基于YANG的配置下发

在本章节,主要是了解YANG是什么,以及基于YANG下发配置的工作原理: 1.什么是YANG 在介绍之前,为了给大家一个最直观的感受,我们打开yang工具,它被打包成一个容器了,可以轻松的使用,…

完美解决丨1. **`SyntaxError: invalid syntax`**

SyntaxError: invalid syntax 因为没有符合语法要求,导致报错。 解决办法: 语法错误的原因主要是代码的风格,例如: 左括号或者右括号没有配对;左括号或者右括号没有放在语句的正确位置;缺少冒号&#xff1…

Window10下安装DPDK

由于我装的是vs2019,打开Visual Studio Installer,在可选下,选择Windows 10 SDK,点击修改。 右键此电脑属性,查看Windows10版本。 安装WDK,打开网址https://learn.microsoft.com/zh-cn/windows-hardware/…