C 语言中调用 Go 语言函数(很少使用)

优质
小牛编辑
145浏览
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)
	}
}