GOLANG

https://media.githubusercontent.com/media/irisHYT/ImageHosting0/main/images/1690861870967.webp

条件编译

平台区分

源文件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只能标记循环,而breakcontinue之后的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代码所以会产生链接错误,以包的方式编译就可以了。

sample

用库

需要确保代码可找到库的位置,若不在系统的链接位置,需要用-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 mainmain函数是必须的

回调函数

在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位平台。

Latest Posts