• extern “C“的底层原理和一些思考,C/C++之间的相互调用


    面试被问到了,答得不是很好,学习记录一下。

    1.了解差异

    首先需要知道C++和C究竟有什么差异

    ①编译方式:C一般使用gcc编译,而C++一般使用g++编译;

    C++的函数重载:C是不支持函数重载的,C++实现函数重载的方法是:名字改编/名字修饰

    简单来说:C++中的名字改编是指在编译时会给相同名字但参数不同的函数分配不同的标识符,这样链接器可以区分它们。而一个函数名在 C 中的编译后表示和其在源代码中的名称是相同的。
    这样就导致了CC++之间不能直接进行调用,要解决这一问题,就得靠extern "C"来辅助了。

    2.extern "C"的底层原理

    当编译器遇到 extern "C" 时,它会按照 C 的规则(即不进行名称修饰)来处理被 extern "C" 修饰的代码。这意味着这部分代码的符号在链接时会使用其原始的、未修饰的名称。

    1. extern "C" {
    2. void foo(); // 该函数在链接时将会使用 "foo" 这个名称,而不是一个修饰过的名称。
    3. }

    注意点:

    • 如果你在 C++ 文件中定义了一个 extern "C" 函数,你必须确保这个函数在整个项目中只有一个定义,因为 C++ 不支持 C 语言的函数重载。

    • 当你在 C++ 中包含 C 的头文件时,为了防止名称冲突,通常会在 C 的头文件中加上:

    1. #ifdef __cplusplus
    2. extern "C" {
    3. #endif
    4. // C 函数声明...
    5. #ifdef __cplusplus
    6. }
    7. #endif

    3.C调用C++的代码

    其实也是使用extern "C"

    ①cpp中定义接口函数

    1. // cpp_code.cpp
    2. extern "C" {
    3. void cppFunction() {
    4. // ... 这里是 C++ 代码 ...
    5. }
    6. }

    对于 C 代码可以调用的所有 C++ 函数,你需要提供一个 extern "C" 接口。这将阻止 C++ 编译器对函数名称进行修饰,从而使得 C 代码可以找到和链接这些函数。

    ②公用的头文件

    1. // common_header.h
    2. #ifdef __cplusplus
    3. extern "C" {
    4. #endif
    5. void cppFunction();
    6. #ifdef __cplusplus
    7. }
    8. #endif

    ③C去调用C++头文件的函数

    1. // c_code.c
    2. #include "common_header.h"
    3. int main() {
    4. cppFunction();
    5. return 0;
    6. }

    ④链接

    g++ c_code.o cpp_code.o -o output_program

    当你将 C 和 C++ 代码一起编译和链接时,必须使用 C++ 链接器。这是因为 C++ 代码可能需要 C++ 运行时的某些部分,比如进行静态对象的初始化。

    ⑤异常处理

    其实上面代码有点问题:C 语言没有对 C++ 异常的支持。需要确保 extern "C" 函数不会将任何 C++ 异常传播到调用它的 C 代码。

    1. extern "C" {
    2. void cppFunction() {
    3. try {
    4. // ... C++ 代码,可能会抛出异常 ...
    5. } catch (...) {
    6. // 处理异常,但不再抛出
    7. }
    8. }
    9. }

    大概就是上面这些步骤能够实现C/C++之间的相互调用。

    ⑥其他(来自gpt,参考参考)

    除了上面提到的异常处理,还有很多其他的问题:

    对象管理:C++ 有构造函数和析构函数的概念,而 C 没有。如果 C 代码需要创建或删除 C++ 对象,需要提供专门的 C 接口函数来完成这些操作。

    内存分配:如果在 C++ 代码中分配了内存(例如使用 new),不应在 C 代码中使用 free 来释放它。同样,如果在 C 代码中使用 malloc 分配的内存,不应在 C++ 中使用 delete 来释放。确保内存分配和释放在同一语言环境中进行。

    RTTI和动态类型转换:如果 C++ 代码使用了运行时类型识别 (RTTI) 或动态类型转换 (dynamic_cast), 这可能会对 C 代码产生影响,尤其是如果 C++ 类型信息不完全可用。

    模板:C++ 模板不能直接在 C 代码中使用,但可以为特定的模板实例化提供 C 接口。

    C++特定库:某些 C++ 库功能(如 STL 容器)可能不易于直接从 C 代码中访问。在这种情况下,提供纯 C 的封装函数可能是一个好选择。

    静态初始化:C++ 支持全局或静态对象的构造函数。当这些对象在程序启动时被初始化,C 代码可能尚未准备好或尚未达到预期的状态。确保了解程序的初始化顺序。

    数据对齐:C 和 C++ 可能有不同的数据结构对齐规则和默认访问权限。为确保数据结构在两种语言中的大小和布局相同,你可能需要使用特定的编译器修饰符或指令。

    回调函数:如果 C++ 代码需要 C 代码提供回调函数,确保这些回调函数也被声明为 extern "C",以确保正确的调用约定。

  • 相关阅读:
    Mac下使用Docker快速布署FastGPT实现AI私有知识库
    # 数值计算:三角形积分
    用于工业产品的MIMX8MN1CVPIZAA I.MX 8M NANO 应用处理器
    多厂商的实现不同vlan间通信
    C和指针 第14章 预处理器 14.5 其他指令
    如何用一行CSS实现10种现代布局
    快消品企业数字化转型解决方案
    python:遗传算法(Genetic Algorithm,GA)求解23个测试函数
    Vue2.js迁移到Vue3.js的API变化
    HarmonyOS应用开发学习历程(1)初识DevEco Studio
  • 原文地址:https://blog.csdn.net/weixin_41653613/article/details/133198269