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日

相关文章

  • 如何在在Vue3中使用markdown 编辑器组件

    以下是在Vue3中使用markdown编辑器组件的攻略: 安装markdown编辑器组件 我们可以使用vue-markdown-editor组件,这是一个基于Vue3的markdown编辑器组件。 在终端中输入下列命令安装: npm install vue3-markdown-editor –save 引入组件 在Vue3项目中,可以使用以下代码引入组件:…

    C 2023年5月23日
    00
  • C++ vector的简单实现

    C++ vector的简单实现 在C++中,vector是一种非常常用的容器,它能够动态地保存一组元素(比如整数、浮点数以及自定义类型等)。在本文中,我们将分步讲解如何实现一个简单的vector。 步骤1:定义类和变量 我们首先要定义一个vector类,它可以保存任意类型的元素,使用template<typename T>来定义: templat…

    C 2023年5月23日
    00
  • JavaScript对象拷贝与Object.assign用法实例分析

    JavaScript对象拷贝与Object.assign用法实例分析 在JavaScript编程中,对象拷贝是一项非常重要的任务,因为我们经常需要在代码中使用对象,但由于JavaScript对象的引用特性,往往原始对象会被误修改或者无意间影响其他部分代码,这时候需要做对象拷贝,保持数据的安全完整性。JavaScript的标准库提供了多种深复制或浅复制对象的拷…

    C 2023年5月22日
    00
  • C++ new、delete(new[]、delete[])操作符重载需要注意的问题

    C++中的 new 和 delete 操作符是用来管理动态内存分配的。在某些情况下,我们需要对 new 和 delete 进行重载,以满足我们特定的需求。但是重载这些操作符需要特别注意一些问题。 为什么需要重载? 一些使用场景: 改变内存分配行为,比如使用某种特殊的内存池来提高内存分配性能。 跟踪某些内存分配和释放,例如在调试模式下记录分配的位置和大小,释放…

    C 2023年5月23日
    00
  • C++ move()函数案例详解

    C++ move()函数案例详解 什么是move()函数? move()函数是C++11中提供的一种对于对象进行右值引用(Rvalue Reference)的操作。该函数能够将对象转换成右值引用,实现对象的移动(Move)而非拷贝(Copy)。 为什么需要move()函数? 在C++的编程过程中,我们经常需要对于对象进行拷贝操作,以便进行如参数传递、返回值传…

    C 2023年5月22日
    00
  • C++实现宿舍管理查询系统

    C++实现宿舍管理查询系统攻略 1. 系统介绍 C++实现宿舍管理查询系统是一款基于控制台界面的宿舍管理查询应用。该系统主要用于方便宿舍管理员进行学生入住管理和住宿情况查询。系统功能包括:学生信息录入、住宿信息录入、学生信息查询、住宿信息查询、学生信息删除等。 2. 开发环境 操作系统:Windows 10 编程语言:C++ 集成开发环境:Visual St…

    C 2023年5月23日
    00
  • 逍遥自在学C语言 | 位运算符&的高级用法

    前言 在上一篇文章中,我们介绍了&运算符的基础用法,本篇文章,我们将介绍& 运算符的一些高级用法。 一、人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— 逍遥。 二、位掩码 位掩码是一种用于按位操作的技术 它通过使用一个二进制数(掩码)来屏蔽或保留目标数中的一些特定位 例…

    C语言 2023年4月17日
    00
  • C语言中花式退出程序的方式总结

    下面是一个完整的“C语言中花式退出程序的方式总结”的攻略。 花式退出程序 程序退出是指程序终止运行并离开当前程序。C语言中有很多种方式能够退出程序,下面将以如下方式进行说明: Exit方法 Return方法 异常退出方法 Exit方法 通过调用函数exit()可以让程序直接退出。exit()函数的声明在 stdlib.h 头文件中。 void exit(in…

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