【C++ 语言】 C 与 C++ 兼容 ( extern "C" )(二)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 【C++ 语言】 C 与 C++ 兼容 ( extern "C" )(二)

C 与 C++ 标准输出 :


C 中的标准输出 : 直接调用 printf

printf("Hello");


C++ 中的标准输出 : << 此处是 操作符重载 , cout 在 std 命名空间中 ;

cout << "Hello" << endl;


③ CMakeLists.txt ( 工程目录下 ) : 项目构建配置文件 , 配置 构建工具版本号 , 项目编译所需的源代码 ;


# CMakeList.txt: 001_CMake_1 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
# 将源代码添加到此项目的可执行文件。
add_executable (001_CMake_1 "001_CMake_1.cpp" "001_CMake_1.h")
# TODO: 如有需要,请添加测试并安装目标。




④ CMakeLists.txt ( 总目录下 ) : 顶层的 CMake 文件, 配置全局所有子项目信息 , 这里只有一个子项目 ;


# CMakeList.txt: 顶层 CMake 项目文件,在此处执行全局配置
# 并包含子项目。
#
cmake_minimum_required (VERSION 3.8)
project ("001_CMake_1")
# 包含子项目。
add_subdirectory ("001_CMake_1")





C++ 中直接调用 C 代码 ( 无法解析的外部符号 错误 )


C++ 向下兼容 : C 中大部分代码都可以在 C++ 中直接使用 ; 但是需要做兼容处理 , 不能直接使用 ;



1. 创建测试文件 : 在上述创建的项目中 , 创建 c_extern.c 和 c_extern.h 两个文件 ;



2. c_extern.h 头文件内容 : 在头文件中定义一个带参数的方法 ;


#pragma once
//任意定义一个方法 , 该方法有若干个参数和返回值
int add(int a, int b);



3. c_extern.c 源文件内容 : 在 C 语言文件中实现上述头文件中定义的带参数的方法 ;


#include "c_extern.h"
//实现的头文件中的方法, 用于测试 C 与 C++ 兼容问题
int add(int a, int b)
{
  return 0;
}




4. CMake 配置源码 : 将 “c_extern.c” ( C文件 ) 和 “c_extern.h” ( 头文件 ) 配置到 CMakeLists.txt 中 ;


# CMakeList.txt: 001_CMake_1 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
# 将源代码添加到此项目的可执行文件。
add_executable (001_CMake_1 "001_CMake_1.cpp" "001_CMake_1.h" "c_extern.c" "c_extern.h")
# TODO: 如有需要,请添加测试并安装目标。




5. 执行结果 : 点击 001_CMake_1.exe 选项 , 运行程序 ; 弹出 “生成失败 , 是否要继续调试?” 的对话框 , 此时

image.png



6. 错误提示 : 无法解析在 main 函数中调用的 add 方法 ;


严重性 代码  说明  项目  文件  行 禁止显示状态
错误  LNK1120 1 个无法解析的外部命令  ...\CMakeLists.txt  ...\001_CMake_1.exe 1 
错误  LNK2019 无法解析的外部符号 "int __cdecl add(int,int)" (?add@@YAHHH@Z),
该符号在函数 main 中被引用  
...\CMakeLists.txt  ...\001_CMake_1.cpp.obj 1


在 C++ 源码中直接调用 C 源码 , 一定会报该错误 , 下面分析产生该错误的原因 , 以及如何进行兼容处理 ;




C++ 与 C 编译结果对比


1. 创建对比文件 : 创建 下面 两个文件 , 分别是 C 代码 和 C++ 代码 ;

image.png



① c_code.c :


int add (int a, int b){
  return a+b;
}
int main(){
  return 0;
}


② c_plus_code.cpp :


int add (int a, int b){
  return a+b;
}
int main(){
  return 0;
}


C 和 C++ 中代码内容一模一样 ;



2. 获取 c_code.c 编译过程中的 机器码文件 : 使用 gcc c_code.c -o c_code.o 命令 , 可以获取编译的中间文件 , 输出到 c_code.o 文件中 ;


image.png



3. 获取 C语言文件编译后的 机器码文件中对应的符号 : 使用 nm -A c_code.o 命令 , 可以查看 c_code.o 二进制文件中的符号 ;


image.png


输出详细内容 :


root@ubuntu:~/001_c_c++# gcc c_code.c -o c_code.o
root@ubuntu:~/001_c_c++# 
root@ubuntu:~/001_c_c++# ls
c_code.c  c_code.o  c_plus_code.cpp
root@ubuntu:~/001_c_c++# 
root@ubuntu:~/001_c_c++# nm -A c_code.o
c_code.o:00000000004004d6 T add
c_code.o:0000000000601030 B __bss_start
c_code.o:0000000000601030 b completed.7594
c_code.o:0000000000601020 D __data_start
c_code.o:0000000000601020 W data_start
c_code.o:0000000000400410 t deregister_tm_clones
c_code.o:0000000000400490 t __do_global_dtors_aux
c_code.o:0000000000600e18 t __do_global_dtors_aux_fini_array_entry
c_code.o:0000000000601028 D __dso_handle
c_code.o:0000000000600e28 d _DYNAMIC
c_code.o:0000000000601030 D _edata
c_code.o:0000000000601038 B _end
c_code.o:0000000000400574 T _fini
c_code.o:00000000004004b0 t frame_dummy
c_code.o:0000000000600e10 t __frame_dummy_init_array_entry
c_code.o:00000000004006d0 r __FRAME_END__
c_code.o:0000000000601000 d _GLOBAL_OFFSET_TABLE_
c_code.o:                 w __gmon_start__
c_code.o:0000000000400584 r __GNU_EH_FRAME_HDR
c_code.o:0000000000400390 T _init
c_code.o:0000000000600e18 t __init_array_end
c_code.o:0000000000600e10 t __init_array_start
c_code.o:0000000000400580 R _IO_stdin_used
c_code.o:                 w _ITM_deregisterTMCloneTable
c_code.o:                 w _ITM_registerTMCloneTable
c_code.o:0000000000600e20 d __JCR_END__
c_code.o:0000000000600e20 d __JCR_LIST__
c_code.o:                 w _Jv_RegisterClasses
c_code.o:0000000000400570 T __libc_csu_fini
c_code.o:0000000000400500 T __libc_csu_init
c_code.o:                 U __libc_start_main@@GLIBC_2.2.5
c_code.o:00000000004004ea T main
c_code.o:0000000000400450 t register_tm_clones
c_code.o:00000000004003e0 T _start
c_code.o:0000000000601030 D __TMC_END__
root@ubuntu:~/001_c_c++#



4. 分析上述输出内容 : 由 第一行 c_code.o:00000000004004d6 T add 可以看出 , add 方法编译后的符号为 add ;



5. 获取 c_plus_code.cpp 编译过程中的 机器码文件 : 使用 gcc c_plus_code.cpp -o c_plus_code.o 命令 , 可以获取编译的中间文件 , 输出到 c_plus_code.o 文件中 ;

image.png



root@ubuntu:~/001_c_c++# gcc c_plus_code.cpp -o c_plus_code.o
root@ubuntu:~/001_c_c++# ls
c_code.c  c_code.o  c_plus_code.cpp  c_plus_code.o
root@ubuntu:~/001_c_c++# 
root@ubuntu:~/001_c_c++# nm -A c_plus_code.o
c_plus_code.o:0000000000601030 B __bss_start
c_plus_code.o:0000000000601030 b completed.7594
c_plus_code.o:0000000000601020 D __data_start
c_plus_code.o:0000000000601020 W data_start
c_plus_code.o:0000000000400410 t deregister_tm_clones
c_plus_code.o:0000000000400490 t __do_global_dtors_aux
c_plus_code.o:0000000000600e18 t __do_global_dtors_aux_fini_array_entry
c_plus_code.o:0000000000601028 D __dso_handle
c_plus_code.o:0000000000600e28 d _DYNAMIC
c_plus_code.o:0000000000601030 D _edata
c_plus_code.o:0000000000601038 B _end
c_plus_code.o:0000000000400574 T _fini
c_plus_code.o:00000000004004b0 t frame_dummy
c_plus_code.o:0000000000600e10 t __frame_dummy_init_array_entry
c_plus_code.o:00000000004006d0 r __FRAME_END__
c_plus_code.o:0000000000601000 d _GLOBAL_OFFSET_TABLE_
c_plus_code.o:                 w __gmon_start__
c_plus_code.o:0000000000400584 r __GNU_EH_FRAME_HDR
c_plus_code.o:0000000000400390 T _init
c_plus_code.o:0000000000600e18 t __init_array_end
c_plus_code.o:0000000000600e10 t __init_array_start
c_plus_code.o:0000000000400580 R _IO_stdin_used
c_plus_code.o:                 w _ITM_deregisterTMCloneTable
c_plus_code.o:                 w _ITM_registerTMCloneTable
c_plus_code.o:0000000000600e20 d __JCR_END__
c_plus_code.o:0000000000600e20 d __JCR_LIST__
c_plus_code.o:                 w _Jv_RegisterClasses
c_plus_code.o:0000000000400570 T __libc_csu_fini
c_plus_code.o:0000000000400500 T __libc_csu_init
c_plus_code.o:                 U __libc_start_main@@GLIBC_2.2.5
c_plus_code.o:00000000004004ea T main
c_plus_code.o:0000000000400450 t register_tm_clones
c_plus_code.o:00000000004003e0 T _start
c_plus_code.o:0000000000601030 D __TMC_END__
c_plus_code.o:00000000004004d6 T _Z3addii
root@ubuntu:~/001_c_c++#





6. 分析上述输出内容 : 由 最后一行 c_plus_code.o:00000000004004d6 T _Z3addii 可以看出 , add 方法编译后的符号为 _Z3addii ;



处理完毕后的文件内容 :

image.png

目录
相关文章
|
4月前
|
存储 编译器 C语言
详解C/C++中的static和extern
本文详解了C/C++中`static`和`extern`关键字的用法和区别,通过具体代码示例说明了在不同情境下如何正确使用这两个关键字,以及`extern "C"`在C++中用于兼容C语言库的特殊作用。
119 4
详解C/C++中的static和extern
|
5月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
88 2
|
5月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
94 0
|
3月前
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
63 5
|
3月前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
3月前
|
存储 分布式计算 编译器
深入计算机语言之C++:C到C++的过度-2
深入计算机语言之C++:C到C++的过度-2
|
3月前
|
编译器 Linux C语言
深入计算机语言之C++:C到C++的过度-1
深入计算机语言之C++:C到C++的过度-1
|
4月前
|
JavaScript 前端开发 测试技术
一个google Test文件C++语言案例
这篇文章我们来介绍一下真正的C++语言如何用GTest来实现单元测试。
30 0
|
5月前
|
编译器 C++ 容器
C++语言的基本语法
想掌握一门编程语言,第一步就是需要熟悉基本的环境,然后就是最重要的语法知识。 C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 完整关键字
|
6月前
|
前端开发 编译器 程序员
协程问题之为什么 C++20 的协程代码比其他语言的协程 demo 长很多如何解决
协程问题之为什么 C++20 的协程代码比其他语言的协程 demo 长很多如何解决