Go

Go 知识量:6 - 35 - 115

2.7 CGO内存模型><

Go访问C内存- 2.7.1 -

在Go语言中访问C内存通常涉及到Go的CGo子系统。CGo允许Go代码调用C库函数并与之交互。以下是一个简单的示例,展示了如何在Go中调用C函数并访问其内存:

1. 首先,创建一个C文件,命名为example.c:

#include <stdio.h>  
  
void* allocate(size_t size) {  
    void* ptr = malloc(size);  
    if (ptr == NULL) {  
        fprintf(stderr, "Failed to allocate memory\n");  
        return NULL;  
    }  
    return ptr;  
}  
  
void deallocate(void* ptr) {  
    free(ptr);  
}  
  
void print_memory(void* ptr, size_t size) {  
    char* data = ptr;  
    for (size_t i = 0; i < size; i++) {  
        printf("%02X ", (unsigned char)data[i]);  
    }  
    printf("\n");  
}

2. 接下来,在Go中调用C函数:

package main  
  
/*  
#include "example.h"  
*/  
import "C"  
import "fmt"  
  
func main() {  
    // 分配内存  
    ptr := C.allocate(10)  
    if ptr == nil {  
        fmt.Println("Failed to allocate memory")  
        return  
    }  
    defer C.deallocate(ptr) // 使用defer确保内存被释放  
  
    // 写入数据到内存中  
    C.memcpy(ptr, C.CString("Hello from Go"), 10)  
    fmt.Println("Data in memory:", string(C.GoBytes(ptr, 10))) // 打印内存中的数据  
}

3. 最后,需要生成一个头文件example.h,以便在Go代码中包含它:

#ifndef EXAMPLE_H_INCLUDED  
#define EXAMPLE_H_INCLUDED  
  
void* allocate(size_t size);  
void deallocate(void* ptr);  
void print_memory(void* ptr, size_t size);  
  
#endif // EXAMPLE_H_INCLUDED

4. 编译和运行Go代码:首先,使用gcc编译C代码:gcc -shared -o example.so example.c。然后,使用go命令编译和运行Go代码:go run main.go。这将链接到之前编译的共享库并执行程序。应该能够看到输出中显示了在内存中的数据。

C临时访问传入的Go内存- 2.7.2 -

在C语言中直接访问传入的Go内存是不安全的,因为Go内存的管理机制与C语言不同,容易导致内存错误和未定义的行为。为了在C语言中安全地访问Go内存,需要使用Go提供的C接口函数和类型。

以下是一个示例,展示了如何在C语言中通过Go提供的接口函数和类型访问传入的Go内存:

package main  
  
//export Add  
func Add(a, b int) int {  
    return a + b  
}  
  
//export Multiply  
func Multiply(a, b int) int {  
    return a * b  
}  
  
//export Sum  
func Sum(numbers []int) int {  
    sum := 0  
    for _, num := range numbers {  
        sum += num  
    }  
    return sum  
}  
  
//export PrintIntArray  
func PrintIntArray(arr *[5]int) {  
    for i := 0; i < len(arr); i++ {  
        fmt.Printf("%d ", arr[i])  
    }  
    fmt.Println()  
}

在上面的示例中,定义了几个函数,并在函数名前加上export关键字,以便在C语言中调用它们。这些函数可以接受和返回各种类型的参数,包括基本类型和数组。注意:在函数声明中使用//export注释,以告诉Go编译器生成与C兼容的函数声明。

接下来,在C语言中调用这些函数:

#include <stdio.h>  
#include "example.h" // 包含生成的C头文件  
  
int main() {  
    int result = Add(2, 3); // 调用Add函数,并获取返回值  
    printf("2 + 3 = %d\n", result); // 打印结果  
  
    int numbers[] = {1, 2, 3, 4, 5}; // 定义一个整数数组  
    result = Sum(numbers); // 调用Sum函数,并获取返回值  
    printf("Sum of numbers: %d\n", result); // 打印结果  
  
    PrintIntArray(&numbers); // 调用PrintIntArray函数,并传递数组的指针作为参数  
    return 0;  
}

在上面的C代码中,首先包含了生成的C头文件example.h,以便能够访问Go函数和类型。然后,使用C语言的语法调用了Go函数,并传递了适当的参数。注意:传递了数组的指针作为参数给PrintIntArray函数。这是因为Go中的数组是值类型,而C语言中的数组是引用类型。需要传递数组的指针才能在C中正确地访问它。