Go语言中调用C语言函数
- 在Go语言开篇中我们已经知道, Go语言与C语言之间有着千丝万缕的关系, 甚至被称之为21世纪的C语言
- 所以在Go与C语言互操作方面,Go更是提供了强大的支持。尤其是在Go中使用C,你甚至可以直接在Go源文件中编写C代码,这是其他语言所无法望其项背的
- 格式:
- 在import “C”之前通过单行注释或者通过多行注释编写C语言代码
- 在import “C”之后编写Go语言代码
- 在Go语言代码中通过C.函数名称() 调用C语言代码即可
- 注意: import “C”和前面的注释之间不能出现空行或其它内容, 必须紧紧相连
package main
//#include <stdio.h>
//void say(){
// printf("Hello World\n");
//}
import "C"
func main() {
C.say()
}
package main
/*
#include <stdio.h>
void say(){
printf("Hello World\n");
}
*/
import "C"
func main() {
C.say()
}
- Go语言中没有包名是C的包, 但是这个导入会促使Go编译器利用cgo工具预处理文件
- 在预处理过程中,cgo会产生一个临时包, 这个包里包含了所有C函数和类型对应的Go语言声明
- 最终使得cgo工具可以通过一种特殊的方式来调用import “C”之前的C语言代码
C语言中调用Go语言函数(很少使用)
- 在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同时编译多个文件
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)
}
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)
}
}