# CGO項目中常用的數據轉換怎么使用
## 前言
在Go語言與C語言混合編程(CGO)項目中,數據轉換是最基礎也是最重要的操作之一。由于Go和C是兩種不同的語言體系,它們在數據類型、內存管理、字符串表示等方面存在顯著差異。本文將深入探討CGO項目中常見的數據轉換場景,包括基本數據類型、字符串、數組、結構體、指針等轉換方法,并通過具體示例演示如何正確實現跨語言數據交互。
## 一、基本數據類型轉換
### 1.1 數值類型轉換
Go和C的基本數值類型大多存在直接對應關系:
| C類型 | Go類型 | 說明 |
|----------------|----------|---------------------|
| char | C.char | 通常對應int8 |
| short | C.short | 通常對應int16 |
| int | C.int | 通常對應int32 |
| long | C.long | 平臺相關 |
| long long | C.longlong | 通常對應int64 |
| unsigned char | C.uchar | 通常對應uint8 |
| unsigned short | C.ushort | 通常對應uint16 |
| unsigned int | C.uint | 通常對應uint32 |
| unsigned long | C.ulong | 平臺相關 |
| float | C.float | 32位浮點數 |
| double | C.double | 64位浮點數 |
**轉換示例:**
```go
/*
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
*/
import "C"
import "fmt"
func main() {
a := C.int(10)
b := C.int(20)
result := C.add(a, b)
fmt.Println("Result:", int(result)) // 顯式轉換為Go的int類型
}
C語言沒有內置的bool類型(C99之前),通常用int表示(0為false,非0為true):
/*
int is_positive(int num) {
return num > 0;
}
*/
import "C"
import "fmt"
func main() {
num := C.int(-5)
ret := C.is_positive(num)
goBool := ret != 0 // 將C的int轉換為Go的bool
fmt.Println(goBool)
}
使用C.CString函數轉換,注意需要手動釋放內存:
/*
#include <stdio.h>
void print_string(const char* s) {
printf("%s\n", s);
}
*/
import "C"
import "unsafe"
func main() {
goStr := "Hello, CGO!"
cStr := C.CString(goStr)
defer C.free(unsafe.Pointer(cStr)) // 必須釋放內存
C.print_string(cStr)
}
使用C.GoString或C.GoStringN:
/*
const char* get_greeting() {
return "Hello from C!";
}
*/
import "C"
import "fmt"
func main() {
cStr := C.get_greeting()
goStr := C.GoString(cStr) // 不需要手動釋放
fmt.Println(goStr)
}
對于可能包含null字節的數據,使用C.GoBytes:
/*
const char* get_data() {
return "data\x00with\x00nulls";
}
*/
import "C"
import "fmt"
func main() {
data := C.GoBytes(unsafe.Pointer(C.get_data()), 15)
fmt.Printf("%q\n", data) // "data\x00with\x00nulls\x00"
}
使用unsafe.Pointer進行通用指針轉換:
/*
void* get_pointer(void* p) {
return p;
}
*/
import "C"
import "unsafe"
func main() {
var i int = 42
p := unsafe.Pointer(&i)
cPtr := C.get_pointer(p)
goPtr := unsafe.Pointer(cPtr)
fmt.Println(*(*int)(goPtr)) // 42
}
/*
void* return_null() {
return NULL;
}
*/
import "C"
import "unsafe"
func main() {
cPtr := C.return_null()
if cPtr == nil {
fmt.Println("Got NULL pointer")
}
// 轉換為unsafe.Pointer前檢查nil
if unsafe.Pointer(cPtr) == nil {
fmt.Println("Confirmed NULL")
}
}
/*
void process_array(int* arr, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
}
*/
import "C"
import "unsafe"
func main() {
goSlice := []int32{1, 2, 3, 4, 5}
cArray := (*C.int)(unsafe.Pointer(&goSlice[0]))
C.process_array(cArray, C.int(len(goSlice)))
}
/*
int global_array[5] = {1, 2, 3, 4, 5};
*/
import "C"
import "unsafe"
func main() {
// 獲取C數組指針和長度
cArray := &C.global_array[0]
length := 5
// 轉換為Go切片
slice := (*[1 << 30]int32)(unsafe.Pointer(cArray))[:length:length]
fmt.Println(slice) // [1 2 3 4 5]
}
/*
typedef struct {
int x;
float y;
} Point;
Point create_point(int x, float y) {
Point p = {x, y};
return p;
}
*/
import "C"
import "fmt"
type GoPoint struct {
X int
Y float32
}
func main() {
cPoint := C.create_point(10, 3.14)
goPoint := GoPoint{
X: int(cPoint.x),
Y: float32(cPoint.y),
}
fmt.Printf("%+v\n", goPoint)
}
對于包含指針或數組的結構體:
/*
typedef struct {
char* name;
int scores[3];
} Student;
void print_student(Student* s) {
printf("Name: %s, Scores: %d %d %d\n",
s->name, s->scores[0], s->scores[1], s->scores[2]);
}
*/
import "C"
import "unsafe"
type GoStudent struct {
Name string
Scores [3]int32
}
func main() {
goStudent := GoStudent{
Name: "Alice",
Scores: [3]int32{90, 85, 95},
}
cName := C.CString(goStudent.Name)
defer C.free(unsafe.Pointer(cName))
cStudent := C.Student{
name: cName,
scores: [3]C.int{C.int(goStudent.Scores[0]), C.int(goStudent.Scores[1]), C.int(goStudent.Scores[2])},
}
C.print_student(&cStudent)
}
/*
typedef int (*callback_func)(int);
int run_callback(callback_func f, int arg) {
return f(arg);
}
*/
import "C"
import "fmt"
//export GoCallback
func GoCallback(arg C.int) C.int {
return arg * 2
}
func main() {
result := C.run_callback(C.callback_func(C.GoCallback), 21)
fmt.Println(result) // 42
}
/*
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
typedef int (*math_func)(int, int);
math_func get_func(int op) {
return op == 1 ? add : sub;
}
*/
import "C"
import "fmt"
func main() {
cFunc := C.get_func(1) // 獲取add函數
result := cFunc(30, 12)
fmt.Println(result) // 42
}
func processStrings() {
for i := 0; i < 1000000; i++ {
str := fmt.Sprintf("string %d", i)
cstr := C.CString(str)
// 如果沒有調用C.free,會導致內存泄漏
C.free(unsafe.Pointer(cstr))
}
}
func safeStringConversion(goStr string) {
cStr := C.CString(goStr)
defer C.free(unsafe.Pointer(cStr)) // 確保釋放
// 使用cStr...
}
func danglingPointerExample() {
var cPtr *C.char
{
goStr := "temporary"
cPtr = C.CString(goStr)
C.free(unsafe.Pointer(cPtr)) // 過早釋放
}
// 現在cPtr是一個懸掛指針,使用它是未定義行為
}
// 不好的做法:頻繁轉換
for _, s := range stringSlice {
cStr := C.CString(s)
// ...
C.free(unsafe.Pointer(cStr))
}
// 好的做法:批量處理
cStrings := make([]*C.char, len(stringSlice))
for i := range stringSlice {
cStrings[i] = C.CString(stringSlice[i])
}
defer func() {
for _, cs := range cStrings {
C.free(unsafe.Pointer(cs))
}
}()
var stringPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, 256)
},
}
func getCString(goStr string) *C.char {
buf := stringPool.Get().([]byte)
buf = append(buf[:0], goStr...)
buf = append(buf, 0) // null終止
return (*C.char)(unsafe.Pointer(&buf[0]))
}
/*
int process(int* arr, int len);
*/
// 錯誤:傳遞[]int32給int*
goSlice := []int32{1, 2, 3}
C.process((*C.int)(unsafe.Pointer(&goSlice[0])), C.int(len(goSlice)))
// 正確:確保類型匹配
goSlice := make([]int, 10)
C.process((*C.int)(unsafe.Pointer(&goSlice[0])), C.int(len(goSlice)))
// 處理UTF-8字符串
goStr := "你好,世界"
cStr := C.CString(goStr)
defer C.free(unsafe.Pointer(cStr))
// 如果C函數需要寬字符
/*
#include <wchar.h>
void print_wide(const wchar_t* str);
*/
cWideStr := C.CString(goStr) // 需要額外編碼轉換
defer C.free(unsafe.Pointer(cWideStr))
// C.print_wide((*C.wchar_t)(unsafe.Pointer(cWideStr)))
/*
#if defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
EXPORT int cross_platform_func();
*/
import "C"
func main() {
// 在Windows和Unix-like系統上都能正常工作
C.cross_platform_func()
}
在CGO項目中進行數據轉換時,關鍵點包括:
通過本文介紹的各種轉換技術和最佳實踐,開發者可以在CGO項目中安全高效地實現Go與C之間的數據交互。記住,在混合語言編程中,明確性和安全性應該始終優先于巧妙的技巧。
”`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。