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 选项 , 运行程序 ; 弹出 “生成失败 , 是否要继续调试?” 的对话框 , 此时
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++ 代码 ;
① 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 文件中 ;
3. 获取 C语言文件编译后的 机器码文件中对应的符号 : 使用 nm -A c_code.o 命令 , 可以查看 c_code.o 二进制文件中的符号 ;
输出详细内容 :
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 文件中 ;
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 ;
处理完毕后的文件内容 :