13-6 使用 C++ 调用 C 程序

        在 C++ 工程下,调用 C 语言书写的函数,其具体流程如下:

                1)准备工作,创建 C++ 工程 06.call_from_cpp,其中需要引入两个头文件,一个头文件利用 C 语言实现,一个头文件利用 C++ 语言实现。

                06.call_from_cpp.cpp 文件内容为:

#include <iostream>

#include "factorial.h"
#include "fibonacci.h"

int main() {
  std::cout << Factorial(4) << std::endl;
  std::cout << Fibonacci(8) << std::endl;
  return 0;
}

                 CMakeLists.txt 文件内容为:

cmake_minimum_required(VERSION 3.17)
project(06_call_from_cpp)

set(CMAKE_CXX_STANDARD 17)

include_directories(mathutils/include)  # 设置头文件路径

# 添加可执行程序
add_executable(06_call_from_cpp
        06.call_from_cpp.cpp
        mathutils/factorial.cpp
        mathutils/fibonacci.c)

                注意,在具有多个工程的情况下,只能 load 一个工程下的 CMakeLists.txt 文件。此处 C++ 子工程建立在主工程 Chapter 13 之下,但 Chapter13 主工程和 06.call_from_cpp 子工程二者的 CMakeLists.txt 文件只能有一个能运行。图中运行的是  06.call_from_cpp 子工程的 CMakeLists.txt 文件,故右击主工程 CMakeLists.txt 文件,可发现选项:Load CMake Project.

                 2)此时若直接运行 06.call_from_cpp.cpp,结果如下图所示:提示 Fibonacci() 函数无法找到(Factorial() 函数可以找到)。 表面原因在于 Fibonacci() 函数是使用 C 语言书写

                  如下图所示,找寻 C++ 工程下编译过程中生成的目标文件,即 06.call_from_cpp.cpp.obj,factorial.cpp.obj,fibonacci.c.obj。使用 objdump -t 命令,查看各目标文件中编译的函数名。      

                 06.call_from_cpp.cpp.obj 的查询结果如下,可以发现 Factorial() 和 Fibonacci() 函数对应的函数名为 __Z9Factorial 和 __Z9Fibonacci。

                 factorial.cpp.obj 中生成的 Factorial() 函数的函数名与 06.call_from_cpp.cpp.obj 一致,为 __Z9Factorial。

                 而使用 objdump -t 查询 fibonacci.c.obj 时,其生成的函数名为 _Fibonacci。

                综上所述,06.call_from_cpp.cpp.obj 调用的函数名与 factorial.cpp.obj 生成的函数名一致,而与 fibonacci.c.obj 生成的函数名不一致,导致了程序执行失败。故,C++ 目标文件调用的函数名与 C 目标文件生成的函数名不一致,导致 C 语言书写的函数无法被 C++ 程序获取,此为本质原因。

                3)C++ 和 C 目标文件的符号修饰规则不同,故直接执行程序不可行。解决方式是让 C++ 知道这段程序是 C 语言编写的,按照 C 的风格生成符号名(函数名)。故修改 fibonacci.h 文件:

#ifndef CHAPTER13_MATHUTILS_INCLUDE_FIBONACCI_H_
#define CHAPTER13_MATHUTILS_INCLUDE_FIBONACCI_H_

#ifdef __cplusplus  // 如果是 C++ 引用该段代码,则添加 extern "C"
extern "C" {
#endif

  int Fibonacci(int n);

#ifdef __cplusplus
};
#endif

#endif //CHAPTER13_MATHUTILS_INCLUDE_FIBONACCI_H_

                 修饰之后,利用 objdump -t 查看 06.call_from_cpp.cpp.obj,可以发现 C++ 目标文件中关于 Fibonacci() 函数的命名发生了变化。

                且程序成功执行,其结果为:

       需要注意的一点是,上述所有操作均在 mingw 编译器下完成。mingw 编译器和 msvc 编译器对符合的命名规则是不同的,但两者的解决方案是一致的。添加 extern "C" 后,msvc 和 mingw 编译器都可正常执行上述程序。

        题外话,06.call_from_cpp.cpp.obj 中有许多未知的函数名,可以使用 c++filt.exe 命令查看修饰后函数名的原始函数。 例如上图中的__ZSt4cout 函数,使用 c++filt.exe 命令的结果如下。说明该函数的原始名称为 std::cout。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值