【C++ 语言】 C 与 C++ 兼容 ( extern C )
文章目錄
- 創建項目
- 項目源碼說明
- C++ 中直接調用 C 代碼 ( 無法解析的外部符號 錯誤 )
- C++ 與 C 編譯結果對比
- extern "C" 在頭文件中的標準用法
- 最終的 C / C ++ 兼容 代碼
創建項目
創建 并運行 CMake 項目 :
-
1. 選擇創建選項 : 在打開的歡迎界面中 , 點擊 右側最下方的 "創建新項目 " 選項 ;
-
2. 選擇項目類型 : 選擇創建 “CMake 項目” , 在 Android 中主要使用的也是 CMake 配置 NDK C/C++ 代碼 ;
-
3. 項目配置 : 輸入項目名稱 , 選擇項目位置 , 下面的解決方案名稱會自動生成 , 不勾選最下方的選項 ; 點擊 “創建” 按鈕 , 創建項目 ;
等待項目創建完畢 , 會自動跳轉到程序主界面 ;
- 4. 自動生成解決方案 : 進入程序主界面后 , 系統會自動生成 CMake 解決方案 , 如果一切順利 , 會有如下結果 :
5. 選擇啟動項 : 點擊綠色的小三角按鈕 “選擇啟動項” , 選擇上面生成的解決方案 “001_CMake_1.exe” 選項 , 如下圖示 ;
- 6. 運行程序 : 再次點擊 “001_CMake_1.exe” 選項 , 即可運行該控制臺程序 , 在控制臺中打印 “Hello CMake。” ;
項目源碼說明
相關源碼說明 :
① 001_CMake_1.h : 項目頭文件 ;
// 001_CMake_1.h: 標準系統包含文件的包含文件 // 或項目特定的包含文件。#pragma once#include <iostream>// TODO: 在此處引用程序需要的其他標頭。② 001_CMake_1.cpp : 項目主代碼文件 ;
// 001_CMake_1.cpp: 定義應用程序的入口點。 //#include "001_CMake_1.h"using namespace std;int main() {cout << "Hello CMake。" << endl;return 0; }C 與 C++ 標準輸出 :
- C 中的標準輸出 : 直接調用 printf
- C++ 中的標準輸出 : << 此處是 操作符重載 , cout 在 std 命名空間中 ;
③ 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 ;
處理完畢后的文件內容 :
7. 總結 :
- ① 編譯結果對比 : C 語言 add 方法編譯后的符號是 add , C++ 編譯后的符號是 _Z3addii , 顯然二者不能互相調用 , 因此一旦在 C++ 中調用 add , 就會出現上述無法解析外部符號錯誤 ;
- ② 兼容 : 如果在 C++ 文件中調用 C 語言庫 , 需要做兼容處理 ;
- ③ 示例 : 在 Android 中的 NDK 接口是 C++ 語言的 , 但是調用的庫 如 OpenSL ES , FFMPEG 等都是 C語言的庫 , 因此這里就需要用到 C 與 C++ 的兼容 ;
- ④ 兼容方法 : 使用 extern “C”{} 指定讓大括號中的內容 以 C 語言的方式進行編譯 ; 這樣才能在 C++ 中找到對應的 C 語言中的函數 ; 如下示例 :
編譯過程 : 預處理 -> 編譯 -> 匯編 -> 鏈接;
1. 編譯預處理 : 產生 .i 后綴的預處理文件;
2. 編譯操作 : 產生 .s 后綴的匯編文件;
3. 匯編操作 : 產生 .o 后綴的機器碼二進制文件;
4. 鏈接操作 : 產生可執行文件 ;
extern “C” 在頭文件中的標準用法
extern “C” 用法 :
- 1. 在引用處使用 : extern “C” {} 可以寫在 引用 頭文件的位置 , 如下 :
- 2. 在頭文件中定義 : extern “C” {} 也可以寫在 頭文件 中 , 一般情況下我們編寫的 C 代碼需要同時兼容 C 和 C++ , 因此 C 語言的頭文件都進行如下定義 :
- ① __cplusplus 宏 : 該宏定義在 C++ 編譯器中 , 如果是 C 語言編譯器 , 就不會定義該宏 ;
- ② 使用效果 : 如果在 C++ 編譯環境中 , extern “C” { 和 } 生效 , 在 C 語言編譯環境中 , 不生效 ;
注意上述兼容二選一 , 不能同時使用 , 否則會報錯 ;
最終的 C / C ++ 兼容 代碼
最終的 C / C ++ 兼容 代碼 :
- 1.程序結構 :
- 2.頂層 CMakeLists.txt : 配置多個項目 ;
- 3.項目 CMakeLists.txt : 配置單個項目中的多個源文件 ;
- 4.c_extern.h :
- 5.c_extern.c :
- 7.001_CMake_1.h :
- 8.001_CMake_1.cpp :
- 9.運行結果 :
總結
以上是生活随笔為你收集整理的【C++ 语言】 C 与 C++ 兼容 ( extern C )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 应用开发】Paint
- 下一篇: 【C++ 语言】引用数据类型 ( 引用数