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语言代码
-
常规问题:
-
如果编译报错
cc1.exe: sorry, unimplemented: 64-bit mode not compiled in - 说明你使用的是64的golang,而你使用的32位的MinGW,所以需要下载64位的mingw并配置环境变量
- 下载地址: https://www.baidu.com/s?wd=sorry%2C%20unimplemented%3A%2064-bit%20mode%20not%20compiled%20in
-
如果编译报错
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同时编译多个文件
- 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.GoString(str) 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)
}
-
结构体类型
结构体变量.属性名称
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)
}
}
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。