C++中的extern “C”用法详解

C++中的extern "C"用法详解

简介

在C++中,存在着C和C++的二进制兼容性问题,即C++编译后的函数名与C编译后的函数名不一样。这会导致当我们在头文件中声明一个C++函数的时候,在C语言中无法使用这个函数。所以我们需要在C++ 中使用 extern "C" 关键字声明特定函数,以便在 C++ 环境下使用 C 标准程序声明及定义的函数。

用法

使用 extern "C" 关键字前需要知道两个概念:

  • 名称修饰:C++ 编译器对函数名的处理是以一种叫做“名称修饰(Name Mangling)”的方式来实现的,这种方式的本质是在函数名前面加上符号“_”或者类似于“_Z”的字符来实现的。而在C语言中,由于不存在像C++中的函数重载等机制,所以编译器对函数名不做任何修饰。
  • 函数签名:函数签名是指函数的类型信息,包括函数类型、参数列表、参数类型和返回类型等信息。

由于名称修饰的存在,我们定义的函数名与C语言的原型并不一致。通过 extern "C" 关键字,我们可以将名称修饰去掉,使得C语言可以调用这个函数并链接成功。

下面我们来看看 extern "C" 的使用例子。

示例1:在C++代码中定义函数并在C中使用

假设我们在C++中有以下代码:

// Sum.cpp
#include <iostream>
#include "sum.h"

int sum(int a, int b) {
  std::cout << "Sum function in C++ is being called." << std::endl;
  return a + b;
}

其中,头文件 sum.h 的内容为:

// sum.h
#ifndef SUM_H
#define SUM_H

extern "C" int sum(int a, int b);

#endif

在我们编写完以上代码后,在C语言中可以通过 below 的方式调用:

// main.c
#include <stdio.h>
#include "sum.h"

int main(void) {
  int result = sum(1, 2);
  printf("%d\n", result);
  return 0;
}

则在链接的时候会提示找不到sum函数的符号,这个时候使用 extern "C" 来解决。

// Sum.cpp
#include <iostream>
#include "sum.h"

// 添加 extern "C" 修饰符
extern "C" {
  int sum(int a, int b) {
    std::cout << "Sum function in C++ is being called." << std::endl;
    return a + b;
  }
}

Sum.cpp 重新编译,并且在相应C编译器的编译选项(-I选项)中添加 sum.h 的头文件路径,即可在C中使用。

示例2:C++和C通过动态库进行交互

前面一节只是简单地通过头文件来解决问题。我们还可以通过动态库(或称为共享库)的方式,将需要提供C语言调用的接口打包成一个动态库,让C语言程序调用该动态库,以进行功能的扩展。

在这里我们同样以 sum 函数为例,来展示C++和C之间通过动态库进行交互的方法。

// Sum.cpp
#include <iostream>

extern "C" {
  int getSum(int a, int b) {
    std::cout << "Sum function in dynamic library is being called." << std::endl;
    return a + b;
  }
}

在这里,我们将 getSum 作为动态库中的函数提供给C语言调用。

接下来,我们先将 getSum 字符串名称修饰去掉:

// Sum.cpp
#include <iostream>

extern "C" {
  // 去掉名称修饰
  __declspec(dllexport) int __stdcall getSum(int a, int b) {
    std::cout << "Sum function in dynamic library is being called." << std::endl;
    return a + b;
  }
}

其中,__declspec(dllexport) 是 Windows 下的一个关键字(类似于 __attribute__((visibility("default"))),用于指定该函数导出的符号),因为动态库在编译的时候需要明确地指定导出和导入,这也是和静态库最大区别。

接下来,我们可以通过以下步骤来实现动态库的编译,以及C语言程序的调用:

1.编译 Sum.cpp,产生 Sum.obj 文件:

> g++ Sum.cpp -fPIC -c -o Sum.o

其中,-fPIC 表示生成位置独立的代码,防止反汇编时找不到详细地址等信息。

2.使用下列命令将 Sum.obj 文件生成动态库文件 libsum.so:

> g++ -shared -o libsum.so Sum.o

3.编写C程序,调用动态库:

// main.c
#include <stdio.h>
#include <dlfcn.h>

int main() {

    void *dlib = dlopen("./libsum.so", RTLD_NOW);

    if (dlib == NULL) {
        fprintf(stderr, dlerror()); 
        exit(1);
    }

    typedef int (*funcptr)(int, int);

    funcptr func = (funcptr)dlsym(dlib, "getSum");

    printf("%d\n", (*func)(1, 2));

    dlclose(dlib);

    return 0;
}

其中, dlfcn.h头文件包含了动态链接库的头文件,dlopen 函数可以用来打开动态库,dlsym函数的参数 getSum 是动态库中提供给C语言的函数名,dlclose 是关闭链接。

通过上述步骤,我们实现了C++和C之间通过动态库进行交互的方法。

结论

通过在C++中使用 extern "C" 关键字,我们可以在C++环境下使用C标准程序声明及定义的函数,并且可以通过动态库的方式提供给C语言进行调用。通过多种实例的讲解,相信读者已经对C++中的 extern "C" 用法有了更为详细的理解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中的extern “C”用法详解 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C++ 实现LRU 与 LFU 的缓存算法

    C++ 实现LRU 与 LFU 的缓存算法 算法描述 LRU和LFU是常用的缓存算法。它们能够优化系统读写速度,提高系统效率。 LRU LRU (Least Recent Used)是最近最少使用算法,维护一个缓存队列,每次访问缓存中的一个元素时,将其移动到队列的头部,当缓存队列满时删除队尾元素,保证最近使用过的元素在缓存队列的最前面,最近没有使用过的元素在…

    C 2023年5月22日
    00
  • 一问学会QT时间类

    如何学习QT时间类 一、了解QT时间类 QT时间类是QT框架提供的一个用于处理时间的类,它提供了很多便捷的方法来进行时间计算和转换,并且支持不同的时间格式。其中最常用的时间类有QDateTime、QTime和QDate。 二、基本使用方法 2.1 获取当前时间 使用QDateTime::currentDateTime()函数可以获取当前的时间。 QDateT…

    C 2023年5月23日
    00
  • c# 如何实现一个简单的json解析器

    C# 如何实现一个简单的 JSON 解析器 简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端数据传输。在 C# 中,我们可以通过 Newtonsoft.Json 库方便地进行 JSON 的序列化和反序列化。但是,如果我们想自己实现一个简单的 JSON 解析器,该怎么做呢?本文将在讲解基本原理的同时…

    C 2023年5月23日
    00
  • ASP调用WebService转化成JSON数据,附json.min.asp

    ASP调用WebService转化成JSON数据,可以通过以下步骤完成: 创建一个WebService 在Visual Studio中创建一个WebService项目,添加一个Web服务方法,例如: [WebMethod] public string HelloWorld(string name) { return "Hello " + …

    C 2023年5月23日
    00
  • 联想小新Pro 13笔记本怎么样 联想小新Pro 13笔记本拆机+评测

    联想小新Pro 13 笔记本 联想小新Pro 13 笔记本是一款轻薄便携的高性能笔记本电脑,采用了第8代英特尔酷睿i5/i7处理器、全新独立显卡和全高清显示屏等最新的硬件配置,极大地提升了其性能和使用体验。同时,联想小新Pro 13 笔记本还拥有不错的外观设计和使用续航能力,深受广大用户的喜爱。 联想小新Pro 13 笔记本拆机 步骤1 – 拆卸电池 首先关…

    C 2023年5月22日
    00
  • Win7旗舰版升级Win10提示错误代码C1900107的解决方法

    下面是详细讲解“Win7旗舰版升级Win10提示错误代码C1900107的解决方法”的完整攻略。 问题描述 在升级Win7旗舰版到Win10时,可能会出现错误代码C1900107的提示,导致升级失败。这个错误通常是由于系统内存不足或硬盘空间不足所导致的。 解决方法 针对这个问题,可以采取以下几个步骤来解决: 步骤1:清理硬盘空间 由于Win10系统占用的空间…

    C 2023年5月23日
    00
  • C++实现中值滤波的示例代码

    下面我将为您详细讲解C++实现中值滤波的示例代码的完整攻略。 什么是中值滤波? 中值滤波是一种基本的数字图像处理方法,它是一种非线性滤波器,可以消除图像中的噪声,保持边缘细节。中值滤波的原理是对滤波器窗口中的像素点进行排序,然后取中间的数值作为滤波结果。通常情况下,中值滤波器的窗口大小是一个奇数,如3×3、5×5等等。 C++中值滤波示例代码 在C++中实现…

    C 2023年5月23日
    00
  • 程序员都不知道C语言中的这些小细节

    当我们学习C语言时,很容易掌握其基本语法,包括变量定义、赋值、循环、逻辑运算等操作。然而,在实际开发中,可能会涉及到一些C语言中的小细节,这些细节甚至有可能被一些经验丰富的程序员所忽略。接下来,我们详细讲解“程序员都不知道C语言中的这些小细节”的攻略。 1. 整型溢出 C语言中整型变量通常分为有符号整型和无符号整型。有符号整型可以表示负数,而无符号整型只能表…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部