Golang 與 C/C++ 交互實踐

在軟件開發的實際場景中,我們常常會遇到需要將不同語言的優勢結合起來的情況。Golang 憑借其高效的并發性能和簡潔的語法,在網絡編程和系統開發領域備受青睞;而 C/C++ 則以其強大的底層操作能力,在系統資源管理方面具有獨特優勢。那么,如何讓 Golang 調用 C/C++ 代碼,實現二者的優勢互補呢?本文將通過一個實際案例,詳細介紹 Golang 調用 C/C++ 代碼的方法和實現過程。

一、Golang 調用 C/C++ 代碼的基本原理

Golang 調用 C/C++ 代碼主要基于其內置的 "cgo" 工具。cgo 是 Golang 提供的一個特殊工具,它允許在 Golang 代碼中直接嵌入 C 代碼,并在編譯時通過系統的 C/C++ 編譯器進行處理。這種機制使得 Golang 能夠調用 C/C++ 編寫的函數和使用其數據結構,從而實現不同語言間的無縫協作。

在使用 cgo 時,我們需要注意以下幾個關鍵點:

  • 通過特殊注釋/* #cgo CFLAGS: ... #cgo LDFLAGS: ... */設置編譯和鏈接參數
  • 使用import "C"導入 C 命名空間
  • 注意 C/C++ 與 Golang 之間的類型轉換,例如 C 的int對應 Golang 的C.int,C 的char*對應 Golang 的*C.char

二、實戰案例:實現系統信息獲取功能

下面我們通過一個獲取系統信息的例子,詳細介紹 Golang 調用 C/C++ 代碼的具體步驟。

步驟 1:創建 C/C++ 頭文件

首先,我們需要創建一個頭文件sysconfig.h,聲明我們要調用的系統信息獲取函數。為了確保 C++ 函數能夠被 C 風格調用,我們使用extern "C"進行修飾。

#ifndef __TTU_SYS_CONFIG__
#define __TTU_SYS_CONFIG__#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus *///返回cpu占用率,百分比
extern int getCpuOccupy();//返回內存占用率,百分比
extern int getRamOccupy();//調用命令dh獲取磁盤信息,返回磁盤占用百分比
extern int getDiskOccupy();//獲取設備運行時間,返回秒
extern unsigned long getRunTime();//獲取啟動時間,格式 2019-01-02 10:43:57
extern int getUPTime(char * buffer,int buffersize);//獲取設備當前時間,格式 2019-01-02 10:43:57
extern int getTime(char * buffer,int buffersize);//設置設備當前時間,格式 2019-01-02 10:43:57
extern int setTime(char * time);//獲取,設備類型
extern int getDevType(char * buffer,int buffersize);//獲取,設備名稱
extern int getDevName(char * buffer,int buffersize);//獲取,設備狀態
extern int getDevStatus(char * buffer,int buffersize);//獲取,設備廠商信息
extern int getDevVendor(char * buffer,int buffersize);//獲取,硬件版本
extern int getHardwareVer(char * buffer,int buffersize);//獲取,平臺軟件版本
extern int getSoftwareVer(char * buffer,int buffersize);#ifdef __cplusplus
}
#endif /* __cplusplus */#endif

步驟 2:實現 C/C++ 函數

接下來,我們創建sysconfig.cc文件實現這些函數。這里僅展示部分實現示例:

#include "sysconfig.h"
#include <sys/sysinfo.h>
#include <time.h>
#include <string.h>
#include <unistd.h>int getCpuOccupy() {// 實現CPU占用率計算邏輯return 42; // 示例返回值
}int getRamOccupy() {struct sysinfo info;if (sysinfo(&info) != 0) {return -1;}return (info.totalram - info.freeram) * 100 / info.totalram;
}// 其他函數實現...

步驟 3:編譯生成共享庫

將 C/C++ 代碼編譯為共享庫文件(.so),使用 - fPIC 選項生成位置無關代碼,-shared 選項生成共享庫:

g++ -fPIC -shared sysconfig.cc -o libsysconfig.so

步驟 4:Golang 調用實現

最后,編寫 Golang 代碼調用這些 C 函數:

package mainimport ("fmt""unsafe""time"
)/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L./ -lsysconfig
#include "sysconfig.h" //非標準c頭文件,所以用引號
*/
import "C"func main() {// 獲取基本系統信息cpu := C.getCpuOccupy()mem := C.getRamOccupy()disk := C.getDiskOccupy()devrun := C.getRunTime()fmt.Println("系統信息:")fmt.Printf("CPU占用率: %d%%\n", cpu)fmt.Printf("內存占用率: %d%%\n", mem)fmt.Printf("磁盤占用率: %d%%\n", disk)fmt.Printf("設備運行時間: %d秒\n", devrun)// 獲取帶緩沖區的字符串信息getStringInfo := func(cFunc func(*C.char, C.int) C.int, desc string) {bufSize := C.int(128)buf := make([]byte, bufSize)outlen := cFunc((*C.char)(unsafe.Pointer(&buf[0])), bufSize)fmt.Printf("%s: %s (長度: %d)\n", desc, string(buf[:outlen]), outlen)}getStringInfo(C.getUPTime, "啟動時間")getStringInfo(C.getDevType, "設備類型")getStringInfo(C.getDevName, "設備名稱")getStringInfo(C.getDevStatus, "設備狀態")getStringInfo(C.getDevVendor, "設備廠商")getStringInfo(C.getHardwareVer, "硬件版本")getStringInfo(C.getSoftwareVer, "軟件版本")// 設置當前時間示例tm := time.Now().Format("2006-01-02 15:04:05")cs := C.CString(tm)defer C.free(unsafe.Pointer(cs))ret := C.setTime(cs)if ret != 0 {fmt.Println("設置時間失敗")} else {fmt.Println("成功設置系統時間為:", tm)}
}

三、關鍵技術點解析

內存管理要點

在 Golang 與 C/C++ 交互時,內存管理是一個關鍵問題。Golang 有垃圾回收機制,而 C/C++ 需要手動管理內存。因此,在傳遞字符串等數據時,要特別注意避免內存泄漏。例如:

  • 使用C.CString()創建的 C 字符串,需要在使用后調用C.free()釋放
  • 從 C 返回的字符串指針,如果是動態分配的,也需要在 Golang 中釋放

類型轉換技巧

由于 Golang 與 C/C++ 的類型系統不同,需要進行適當的類型轉換:

  • 基本數據類型(如整數、浮點數)轉換相對簡單
  • 字符串和指針類型需要使用unsafe.Pointer進行中轉
  • 數組和切片的轉換需要特別小心,確保內存布局一致

錯誤處理方法

C/C++ 函數通常通過返回特殊值或設置錯誤碼來表示失敗。在 Golang 中調用時,應檢查這些返回值并轉換為 Golang 的錯誤類型:

ret := C.someFunction()
if ret != 0 {return fmt.Errorf("C函數調用失敗,錯誤碼: %d", ret)
}

四、常見問題及解決方案

編譯問題

  • 找不到頭文件:檢查 CFLAGS 是否正確設置頭文件路徑
  • 找不到庫文件:檢查 LDFLAGS 是否正確設置庫文件路徑和名稱
  • 符號沖突:確保 C++ 函數使用 extern "C" 聲明

運行時問題

  • 找不到共享庫:設置 LD_LIBRARY_PATH 環境變量或使用 - rpath 選項
  • 段錯誤:檢查指針操作和內存訪問是否合法
  • 內存泄漏:確保動態分配的內存被正確釋放

性能問題

  • 減少跨語言調用次數,批量處理數據
  • 避免頻繁的字符串轉換,使用緩沖區復用技術
  • 對于高性能場景,考慮使用 Go 重寫關鍵算法

五、應用場景拓展

Golang 調用 C/C++ 代碼在以下場景中特別有用:

系統底層開發

需要調用系統 C 庫函數時,例如操作文件系統、網絡套接字等。

復用遺留代碼

當項目中存在大量 C/C++ 遺留代碼時,可以通過 Golang 調用這些代碼,避免重新開發。

高性能計算

對于計算密集型任務,可以使用 C/C++ 實現核心算法,然后通過 Golang 調用,充分發揮 C/C++ 的性能優勢。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/85534.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/85534.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/85534.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

五子棋流量主小程序單模式多模式開源版

功能和特點&#xff1a; 核心游戲功能&#xff1a; 1515 標準棋盤 黑白棋交替落子 自動判斷勝負和平局 悔棋功能 計時功能 UI 設計&#xff1a; 木紋風格棋盤 立體感棋子&#xff08;使用陰影和漸變&#xff09; 響應式布局&#xff0c;適配不同屏幕尺寸 勝利彈窗動畫 交互體驗…

Python古代文物成分分析與鑒別研究:灰色關聯度、嶺回歸、K-means聚類、決策樹分析

原文鏈接&#xff1a;tecdat.cn/?p42718分析師&#xff1a;Gan Tian 在文化遺產保護領域&#xff0c;古代玻璃制品的成分分析一直是研究中西方文化交流的關鍵課題。作為數據科學家&#xff0c;我們在處理某博物館委托的古代玻璃文物保護咨詢項目時&#xff0c;發現傳統分析方法…

RabbitMQ消息隊列實戰指南

RabbitMQ 是什么&#xff1f; RabbitMQ是一個遵循AMQP協議的消息中間件&#xff0c;它從生產者接收消息并傳遞給消費者&#xff0c;在這個過程中&#xff0c;根據路由規則進行消息的路由、緩存和持久化。 AMQP&#xff0c;高級消息隊列協議&#xff0c;是應用層協議的一個開放…

用Java將PDF轉換成GIF

為什么要將 PDF 文件轉換為 GIF 圖片&#xff1f; PDF 是一種矢量圖像格式&#xff08;因此可以根據指定的尺寸進行渲染&#xff09;&#xff0c;而 GIF 是一種有損的、固定尺寸的位圖文件&#xff0c;像素值固定。因此&#xff0c;將 PDF 轉換為 GIF 文件時&#xff0c;我們需…

Redis之分布式鎖(2)

上一篇文章我們介紹了什么是分布式鎖和分布式鎖的一些基本概念。這篇文章我們來講解一下基于數據庫如何實現分布式鎖。 基于數據庫實現分布式鎖 基于數據庫實現分布式鎖可以分為兩種方式&#xff0c;分別是基于數據庫表和基于數據庫排他鎖。 基于數據庫表 要實現分布式鎖&…

智能檢測護航電池產業:容量設備如何提升效率與安全?

電池容量是衡量其儲能能力的重要指標&#xff0c;直接影響設備續航與使用壽命。電池容量檢測設備通過模擬真實使用場景&#xff0c;精準測量電池的充放電性能&#xff0c;為電池生產、質檢及回收環節提供關鍵數據支持&#xff0c;成為保障電池品質與安全的核心工具。 核心功能…

介紹一款免費MES、開源MES系統、MES源碼

一、系統概述&#xff1a; 萬界星空科技免費MES、開源MES、商業開源MES、市面上最好的開源MES、MES源代碼、適合二開的開源MES。 1.萬界星空開源MES制造執行系統的Java開源版本。 開源mes系統包括系統管理&#xff0c;車間基礎數據管理&#xff0c;計劃管理&#xff0c;物料控制…

構建高性能日志系統:QGroundControl日志模塊深度解析

引言&#xff1a;日志系統的重要性 在無人機地面站系統中&#xff0c;日志記錄是診斷問題、分析性能的關鍵基礎設施。QGroundControl&#xff08;QGC&#xff09;作為領先的開源無人機地面站軟件&#xff0c;其日志系統設計值得深入探討。本文將揭示QGC日志系統的核心技術&…

k8s查看內存占用前十的20個pod服務,不包括job

在 Kubernetes 中&#xff0c;您可以使用 kubectl 命令結合一些工具來查看內存占用前十的 Pod 服務&#xff0c;并排除 Job 類型的 Pod。以下是一個示例命令&#xff0c;您可以在終端中運行&#xff1a; kubectl top pods --all-namespaces --no-headers | grep -v job | sort …

Spring Boot 集成 LangChain4j 示例

文章目錄 概述一、DeepSeek API Key 獲取二、Spring Boot 集成 LangChain4j 示例三、拓展建議 概述 LangChain4j 是 LangChain 在 Java 生態下的實現&#xff0c;它是一個開源庫&#xff0c;幫助你更方便地在 Spring Boot 應用中集成大語言模型&#xff08;如 OpenAI 的 GPT-4…

數據差異的iOS性能調試:設備日志導出和iOS文件管理

在復雜iOS項目中&#xff0c;尤其是集成多個第三方服務、使用混合數據源&#xff08;本地遠程緩存&#xff09;的系統里&#xff0c;“數據不一致”類問題極具迷惑性。一方面&#xff0c;數據看似可用&#xff0c;邏輯層也沒有明顯錯誤&#xff1b;另一方面&#xff0c;用戶層面…

二進制與生活:從數字世界到人生哲理

二進制與生活&#xff1a;從數字世界到人生哲理 最近重溫《少年謝爾頓》&#xff0c;被劇中謝爾頓與二進制對話的場景深深打動。這讓我思考&#xff1a;二進制這個看似冰冷的數字系統&#xff0c;其實與我們的生活有著千絲萬縷的聯系。今天&#xff0c;讓我們一起走進二進制的世…

基于SMB協議的內網存活主機探測技術研究

一、 技術背景 SMB(Server Message Block)協議是Windows環境中廣泛使用的網絡文件共享協議&#xff0c;默認開放于445端口。由于其在Windows系統中的核心地位&#xff0c;SMB協議常被用作內網探測的重要切入點。本文系統介紹多種基于SMB的存活主機探測技術&#xff0c;幫助安全…

IDEA21中文亂碼解決辦法

我改了很多&#xff0c;可能也改了一些沒用的 1.在VM options中添加-Dstdout.encodingUTF-8 -Dstderr.encodingUTF-8 2.IDEA 控制臺輸出設置為 UTF-8 打開 IDEA → File → Settings&#xff08;或 CtrlAltS&#xff09; 搜索 "Encoding" 設置 Project Encoding 和…

時序數據庫概念及IoTDB特性詳解

一、數據庫管理系統概述 數據&#xff0c;如同空氣般普遍存在于我們的數字生活中&#xff0c;每一次點擊手機都可能產生并記錄數據。這些數據被存儲在數據庫中&#xff0c;而數據庫實質上是“數據的集合”。數據庫管理系統&#xff08;DBMS&#xff09;則負責這些“數據容器”…

leetcode:263. 丑數(python3解法,數學相關算法題)

難度&#xff1a;簡單 丑數 就是只包含質因數 2、3 和 5 的 正 整數。 給你一個整數 n &#xff0c;請你判斷 n 是否為 丑數 。如果是&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 示例 1&#xff1a; 輸入&#xff1a;n 6 輸出&#xff1a;true 解釋&am…

RK3568筆記八十五:LVGL播放AVI視頻

若該文為原創文章,轉載請注明原文出處。 最近有個需求,需要播放視頻,但使用的框架是LVGL顯示,所以了解了下LVGL怎么實現播放視頻。 目前了解到的方法有兩種: 一、使用ffmpeg方式,此方法適用于大部分視頻 二、使用opencv方式,此方法適用于大部分視頻 三、使用woshin…

stm32使用定時器PWM

1、定時器TIM PSC-Prescaler-預分頻器 CNT-Counter-計數器 ARR-Auto Reload Register-自動重裝寄存器 RCR-Repetition Counter Register-重復計數器 1、時鐘來源:晶振提供頻率,時鐘樹這些才提供時鐘 分頻系數 計數 3、實例 上面展示了假設使用外部石英晶振提供32.76…

2.3 Windows Vcpkg+MSVC編譯FFmpeg 4.4.1

一、vcpkg安裝ffmpeg 4.4.1 vcpkg的使用可以參考之前的文章&#xff1a;vcpkg 使用 1.1 查看vcpkg中的ffmpeg版本 查看庫的版本&#xff1a;vcpkg.io 1.2 vcpkg.json文件解析 創建vcpkg.json文件&#xff1a; {"builtin-baseline": "984f9232b2fe0eb94f…