網站首頁 編程語言 正文
前言
在項目開發過程中,我們底層代碼經常用C來實現,而上層應用大都會用C++實現,這樣我們就涉及到了C和C++相互調用的情況了。那么,C/C++如何實現相互調用呢?
1、為什么會有差異?
-
編譯方式不同:
C
文件常采用gcc
編譯,而Cpp
文件常采用g++
來編譯C++
-
支持函數重載:由于這一特性,
C++
和C
中的同一個函數,經過編譯后,生成的函數名稱是不同的。
這樣就導致了C
與C++
之間不能直接進行調用,要解決這一問題,就得靠extern "C"
來輔助了。
2、extern “C”
- extern
extern
關鍵字我們并不陌生,它是編程語言中的一種屬性,用來表示變量,函數等類型的作用范圍。
我們經常在
.c
源文件中定義變量或者實現函數,在.h
頭文件中使用extern
關鍵字進行聲明,方便其他文件調用。
“C”
編程語言種類繁多,不同語言有不同的編譯規則,如果想要互相調用,必須告訴編譯器以什么規則去編譯文件,這樣才能正常調用。
其主要作用是:把“C”
當作一個標志位,告訴編譯器,下面代碼以C
的方式編譯!
了解其中原理后,我們來實操一下!
3、C++調用C
我們創建3個文件,分別為main.cpp
、cal.c
、cal.h
。
我們分別使用gcc
和g++
單獨編譯文件,編譯出cal.o
和main.o
兩個中間文件,很簡單,定義了一個embedded_art
的函數。
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:32] $ ls cal.c cal.h main.cpp # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:43] $ gcc -c cal.c # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:49] $ g++ -c main.cpp # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [15:57:55] $ ls cal.c cal.h cal.o main.cpp main.o
下面看一下編譯之后的中間文件cal.o
和main.o
的符號表,看看同一個函數embedded_art
不同編譯方式之后的差別。
可以看到,g++
編譯之后,對函數名稱進行了加工,按照自身的編譯規則,最終生成了一個新的函數名,所以我們如果直接調用cal.c
中的embedded_art
肯定是不行的。
正確方式
使用extern "C"
來使g++
編譯器用C
的方式編譯。
在main.cpp
文件中,我們引入cal.h
的位置,添加extern "C"
extern "C" { #include "cal.h" }
再次進行編譯,即可!
可以看到符號表中,該函數名稱正常,然后我們將中間文件鏈接起來,執行,輸出正確結果!
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:18:36] $ g++ main.o cal.o # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:19:54] $ ls a.out cal.c cal.h cal.o main.cpp main.o # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test on git:main x [16:19:57] $ ./a.out main entry 嵌入式藝術
4、C調用C++
我們創建3個文件,分別為main.c
、cal.cpp
、cal.h
。
我們分別使用gcc
和g++
單獨編譯文件,編譯出cal.o
和main.o
兩個中間文件,很簡單,同樣定義了一個embedded_art
的函數。
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:45] $ g++ -c cal.cpp # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:52] $ gcc -c main.c # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:24:56] $ ls cal.cpp cal.h cal.o main.c main.o
下面看一下編譯之后的中間文件cal.o
和main.o
的符號表,看看同一個函數embedded_art
不同編譯方式之后的差別。
同樣,不同的編譯器處理方式不同,函數名稱依舊不同!同樣,需要加入extern "C"
來告訴編譯器按C
的方式編譯。
我們在cal.h
的聲明部分添加,然后重新編譯!
extern "C" { extern void embedded_art(void); }
可以看到符號表中,該函數名稱正常,然后我們將中間文件鏈接起來。
這個時候,會出現報錯
extern "C"
,這是什么情況?
在main.c
文件中,引入了c++
的頭文件cal.h
,因為"C"
在C++
編譯的時候才能識別,C
語言中并沒有這個關鍵字。
所以,我們需要在g++
編譯的時候去加入extern "C"
,而gcc
編譯的時候跳過,這個時候就要提到c++
編譯時候的特定宏__cplusplus
了,相當于一個閥門了。
我們修改cal.h
文件:
#ifdef __cplusplus extern "C" { #endif extern void embedded_art(void); #ifdef __cplusplus } #endif
這樣就確保了,c++
編譯embedded_art
函數的時候,采用C
語法編譯,而gcc
編譯的時候,不作處理。
再次鏈接,執行!
# dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:45:06] C:1 $ gcc -no-pie cal.o main.o -o main # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:46:46] $ ls cal.cpp cal.h cal.o main main.c main.o # dong @ ubuntu in ~/WorkSpace/Donge_Programs/Unix_Programming_Learning/c_c++_call_test/c_call_c++ on git:main x [16:49:01] $ ./main main entry 嵌入式藝術
補充:C/C++文件之間函數的引用
C通過加"中間層"來引用C++(不用修改原C++文件)
//C文件 int MyMax(int, int); int main() { int a = 10; int b = 20; printf("%d\n", MyMax(a,b)); return 0; }
//C++文件 int Max(int a, int b) { return a > b ? a : b; }
//中間層//C++文件 int Max(int ,int); extern "C" { int MyMax(int a, int b) { return Max(a, b); } }
關鍵點:本文件之間的函數調用,不牽扯到函數符號的生成。比如中間層那個文件:C++格式的聲明,C的引用(return Max(a,b)),不會牽扯到什么鏈接失敗,那是發生在編譯期間的,不牽扯到符號之間的解析。
總結
C/C++
之間的相互調用,歸根到底就是:不同的語言有不同的編譯規則,要想實現通用,就必須告訴編譯器,按照目標語言的規則進行編譯!
原文鏈接:https://blog.csdn.net/dong__ge/article/details/128392355
相關推薦
- 2022-04-18 C語言?簡單粗暴的笨方法找水仙花數_C 語言
- 2022-05-20 python?特有語法推導式的基本使用_python
- 2022-12-24 TS裝飾器bindThis優雅實現React類組件中this綁定_React
- 2022-01-31 RuntimeError:Given input size:(256x1x1). Calculate
- 2023-06-18 Redis優雅地實現延遲隊列的方法分享_Redis
- 2022-07-22 使用@ControllerAdvice和@ExceptionHandler構建全局異常處理器
- 2023-01-21 詳解Go語言如何使用標準庫sort對切片進行排序_Golang
- 2022-07-29 C++超詳細講解操作符的重載_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支