C 语言中调用 Go 语言函数(很少使用)
优质
小牛编辑
149浏览
2023-12-01
在 Go 代码中通过 //export Go函数名称
导出Go的函数名称
在 C 代码中通过 extern 返回值类型 Go函数名称(形参列表);
声明 Go 中导出的函数名称
注意://export Go 函数名称
和 extern 返回值类型 Go函数名称(形参列表);
不能在同一个文件中
package main import "C" import "fmt" // 导出Go函数声明, 给C使用 //export GoFunction func GoFunction() { fmt.Println("GoFunction!!!") }
package main /* #include <stdio.h> // 声明Go中的函数 extern void GoFunction(); void CFunction() { printf("CFunction!\n"); GoFunction(); } */ import "C" func main() { C.CFunction() }
由于不在同一个文件, 所以需要通过go build或者go install同时编译多个文件
Go 中使用 C 语言的类型
基本数据类型
在 Go 中可以用如下方式访问 C 原生的数值类型:
C.char, C.schar (signed char), C.uchar (unsigned char), C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), C.long, C.ulong (unsigned long), C.longlong (long long), C.ulonglong (unsigned long long), C.float, C.double
Go 的数值类型与 C 中的数值类型不是一一对应的。因此在使用对方类型变量时必须显式转型操作
package main /* #include <stdio.h> int num = 123; float value = 3.14; char ch = 'N'; */ import "C" import "fmt" func main() { var num1 C.int = C.num fmt.Println(num1) var num2 int //num2 = num1 // 报错 num2 = int(num1) fmt.Println(num2) var value1 C.float = C.value fmt.Println(value1) var value2 float32 = float32(C.value) fmt.Println(value2) var ch1 C.char = C.ch fmt.Println(ch1) var ch2 byte = byte(C.ch) fmt.Println(ch2) }
字符串类型
- C 语言中并不没有字符串类型,在C中用带结尾 '\0' 的字符数组来表示字符串;而在 Go 中 string 类型是原生类型,因此在两种语言互操作是必须要做字符串类型的转换
- C 字符串转换Go字符串:
C.GoString(str)
- Go 字符串转换C字符串:
C.CString(str)
package main /* #include <stdio.h> char *str = "www.it666.com"; void say(char *name){ printf("my name is %s", name); } */ import "C" import ( "fmt" "unsafe" ) func main() { // 1.C语言字符串转换Go语言字符串 str1 := C.str str2 := C.GoString(str1) fmt.Println(str2) // 2.Go语言字符串转换C语言字符串 str := "lnj" cs := C.CString(str) C.say(cs) // 注意: 转换后所得到的C字符串cs并不能由Go的gc所管理,我们必须手动释放cs所占用的内存 C.free(unsafe.Pointer(cs)) }
指针类型
- 原生数值类型的指针类型可按Go语法在类型前面加上 *,例如:var p *C.int。
- 而 void* 比较特殊,用 Go 中的 unsafe.Pointer 表示。
- unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算
- uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收
- 也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为uintptr 进行指针运算
package main /* #include <stdio.h> int num = 123; void *ptr = # */ import "C" import ( "fmt" "unsafe" ) func main() { // 这是一个C语言变量 var num C.int = C.num // 这是一个C语言指针 var p1 *C.int = &num fmt.Println(*p1) //var p2 *C.void = C.ptr // 报错 // 利用unsafe.Pointer接收viod * var p2 unsafe.Pointer = C.ptr // 将unsafe.Pointer转换为具体类型 var p3 *C.int = (*C.int)(p2) fmt.Println(*p3) }
枚举类型
- C 语言中的枚举类型在 Go 语言中的表现形式为 C.enum_XXX
- 访问枚举和访问普通变量无异,直接通过 C.枚举值 即可
package main /* #include <stdio.h> enum Gender { GenderMale, GenderFemale, GenderYao }; */ import "C" import "fmt" func main() { var sex C.enum_Gender = C.GenderMale fmt.Println(sex) sex = C.GenderFemale fmt.Println(sex) sex = C.GenderYao fmt.Println(sex) }
结构体类型
- C语言中的结构体类型在 Go 语言中的表现形式为 C.struct_XXX
- 访问结构体 直接通过
结构体变量.属性名称
即可
package main /* #include <stdio.h> struct Point { float x; float y; }; */ import "C" import ( "fmt" ) func main() { // 1.利用C的结构体类型创建结构体 var cp C.struct_Point = C.struct_Point{6.6, 8.8} fmt.Println(cp) fmt.Printf("%T\n", cp) // 2.将C语言结构体转换为Go语言结构体 type GoPoint struct { x float32 y float32 } var gp GoPoint gp.x = float32(cp.x) gp.y = float32(cp.y) fmt.Println(gp) }
数组类型
- C语言中的数组与 Go 语言中的数组差异较大, C 中的数组是指针类型,Go 中的数组是值类型
- 目前似乎无法直接显式的在两者之间进行转型,官方文档也没有说明。
package main /* #include <stdio.h> int cArray[5] = {1, 2, 3, 4, 5}; */ import "C" import "fmt" func main() { var cArr [5]C.int = C.cArray fmt.Println(cArr) fmt.Printf("%T\n", cArr) }
利用 Go 语言调用 C 语言函数,实现无缓冲区输入,请在终端运行
package main /* #include <stdio.h> char lowerCase(char ch){ // 1.判断当前是否是小写字母 if(ch >= 'a' && ch <= 'z'){ return ch; } // 注意点: 不能直接编写else, 因为执行到else不一定是一个大写字母 else if(ch >= 'A' && ch <= 'Z'){ return ch + ('a' - 'A'); } return ' '; } char getCh(){ // 1.接收用户输入的数据 char ch; scanf("%c", &ch); setbuf(stdin, NULL); // 2.大小写转换 ch = lowerCase(ch); // 3.返回转换好的字符 return ch; } */ import "C" import "fmt" func main() { for{ fmt.Println("请输入w a s d其中一个字符, 控制小人走出迷宫") var ch byte = byte(C.getCh()) fmt.Printf("%c", ch) } }