12. October 2021
GOLANG
条件编译
平台区分
源文件xxx_$(GOOS)_$(GOARCH).go
仅会在go build GOARCH=$(GOARCH) GOOS=$(GOOS) .
时被编译
源文件xxx_$(GOOS).go
会在go build GOOS=$(GOOS) .
时被编译,这时GOARCH
无论是什么都不会造成影响
源文件xxx_$(GOARCH).go
会在go build GOARCH=$(GOARCH) .
时被编译,这是无论GOOS
是什么都不影响
自定义区分
通过build tag进行条件编译,在文件的第一行,必须是第一行,添加//+build $tag
,然后在编译的时候添加go build -tags=$tag
来编译,这样就只会把头部包含//+build $tag
的源文件编译进去。
当平台区分和自定义区分同时存在,优先进行平台区分
多层跳转
对for循环设置label
,当循环嵌套了多层,需要从最里面的循环跳到最外面或者跳转到指定的某一层循环,可以通过break
/continue
+label
的方式跳转。用来控制循环跳转的label
只能标记循环,而break
和continue
之后的label
只能为标记循环的label
。
1package main
2
3func testbk() {
4first:
5 for {
6 for {
7 break first
8 }
9 print("first inside")
10 }
11 print("first outside")
12}
13
14func main() {
15 testbk();
16}
Go调C
import "C"
之前的注释即为要调用的C语言源码,中间不能有空行或者其他内容。
用源码
C源码在Go文件中
1package main
2
3/*
4
5void hello_world_inside() {
6 printf("hello world inside!\n");
7}
8
9*/
10import "C"
11
12func main() {
13 C.hello_world_inside()
14}
C源码在Go文件外
当c代码头文件和源码分离时,需要注意这种方法不能在main包中使用,只能用于除main包之外的包。,之前不行是因为编译的时候只编译了main.go
,没有编译c代码所以会产生链接错误,以包的方式编译就可以了。
用库
需要确保代码可找到库的位置,若不在系统的链接位置,需要用-L指明。
这里可以用
${SRCDIR}
表示当前go文件所在的位置
1package main
2
3/*
4
5#cgo CFLAGS: -I ${SRCDIR} -std=c99
6#cgo LDFLAGS: -L ${SRCDIR} -lhelloworld
7
8#include "hello-world-lib.h"
9
10*/
11import "C"
12
13func main() {
14 C.hello_world_lib()
15}
C函数调用Go代码
Go生成动/静态库
这种方法指的是通过go代码生成静态或动态库来给c代码调用。
1package main
2
3//extern void hello_world_in_go();
4import "C"
5import "fmt"
6
7//export hello_world_in_go
8func hello_world_in_go() {
9 fmt.Printf("hello world in go!\n")
10}
11
12func main() {}
13
14/// go build -buildmode=c-archive -o helloworld.a main.go 生成静态库和头文件
15/// go build -buildmode=c-shared -o helloworld.so main.go 生成动态库和头文件
-buildmode=c-archive
生成静态库-buildmode=c-shared
生成动态库package main
和main
函数是必须的
回调函数
在C中声明一个回调函数,通过export在go代码中实现c代码中的声明的函数。
1package main
2
3//extern void hello_world_in_go();
4import "C"
5import "fmt"
6
7//export hello_world_in_go
8func hello_world_in_go() {
9 fmt.Printf("hello world in go!\n")
10}
11
12func main() {
13 C.hello_world_in_go()
14}
参数传递
主要的方法就是在Go中定义一个内存占用与C语言中一致的结构体,然后将地址强制转换为C代码中的地址。
传入传出函数
1package main
2
3/*
4struct Bus {
5 int x;
6 int y;
7 int sum;
8};
9
10void add(struct Bus* ps) {
11 ps->sum = ps->x + ps->y;
12}
13*/
14import "C"
15import (
16 "fmt"
17 "unsafe"
18)
19
20type Bus struct {
21 x int32
22 y int32
23 sum int32
24}
25
26func main() {
27 b := &Bus{
28 x: 1,
29 y: 1,
30 }
31
32 var pb *C.struct_Bus = (*C.struct_Bus)(unsafe.Pointer(b))
33 C.add(pb)
34 fmt.Printf("Bus.sum: %d\n", b.sum)
35}
这样做也是可以的,关键的问题就在于确定C中结构体对应的大小,这里的int
在32位或64位的平台下编译都是4个字节的,不知道为啥曾经听说会随着平台改变,查了下,会改变的是short int
,32位2字节64位4字节,golang默认编译的是64位,通过GOGCCFLAGS
这个环境变量就能查到其中-m64
就代表的是64位平台。