CGO简明教程

栏目: Go · 发布时间: 5年前

内容简介:当然这并不是Hello World。我们不首先输出Hello World是有原因的,接下来就会讲到。不过首先我们分析一下现在这个程序。 首先从结构上来看可以知道这就是一个普通的Go程序,第一行这三行是Go调C才这样的,接下来我们看看Hello World。

Hello World

package main

// #include <stdlib.h>
import "C"

import (
    "fmt"
)

func main() {
    fmt.Println(int(C.random()))
}

当然这并不是Hello World。我们不首先输出Hello World是有原因的,接下来就会讲到。不过首先我们分析一下现在这个程序。 首先从结构上来看可以知道这就是一个普通的 Go 程序,第一行 package main 声明这个代码是在main包里。然后下面有 func main 是程序的入口。

// #inlcude <stdlib.h>
import "C"

这三行是Go调C才这样的, import "C" 是为了可以在Go程序里直接使用C里的一些函数,例如main中 C.random() ,而 import "C" 上边的注释叫做preamble,注意必须和 import "C" 紧紧挨着中间不能有空格。注释的风格可以是 // #include... 也可以是 /*#include ...*/ 这样的。此外可以在 preamble 中加入 // #cgo 开头的注释,用于指示编译和链接中发生的一些事情,例如链接哪个动态链接库等。

接下来我们看看Hello World。

首先我们需要三个文件, helloworld.h :

#ifndef __helloworld
#define __helloworld

void Printf(char *s);

#endif

helloworld.c :

#include <stdio.h>

void Printf(char *s) {
    printf("%s", s);
}

main.go :

package main

// #include "helloworld.h"
import "C"

func main() {
    C.Printf("hello world")
}

为啥不直接 C.printf 输出呢,因为在wiki中提到cgo目前暂时还不支持变长参数的C函数,所以要我们自己包装一下。编译:

$ go build
./main.go:7: cannot use "hello world" (type string) as type *_Ctype_char in argument to _Cfunc_Printf

原因是C和Go的字符串不是通用的,我们要把Go的字符串转成C的字符串,但是因为不是在编译的这个过程申请内存,而是在堆里 申请内存存储字符串,而Go的垃圾回收是管不到C申请的内存,所以我们需要自行销毁对应的内存。

package main

// #include <stdlib.h>
// #include "helloworld.h"
import "C"

import (
    "unsafe"
)

func main() {
    cs := C.CString("hello world\n")
    defer C.free(unsafe.Pointer(cs))

    C.Printf(cs)
}

也可以我们先把C编译成动态链接库,然后在Go里指示链接:

package main

// #cgo LDFLAGS: -L${SRCDIR}/ -Wl,-rpath,${SRCDIR}/ -lhelloworld
// #include <stdlib.h>
// #include "helloworld.h"
import "C"

import (
    "unsafe"
)

func main() {
    cs := C.CString("hello world\n")
    defer C.free(unsafe.Pointer(cs))

    C.Printf(cs)
}

我们先把 helloworld.c 编译成动态链接库 libhelloworld.so

类型

C和Go中有很多类型是对应的,但是需要我们自行转换类型。例如:

- C.int
- C.long
- C.ulong

如果是访问struct得这么用 C.struct_<struct的名字> ,enum,union和sizeof也类似。

具体参考: https://golang.org/cmd/cgo/#hdr-Go_references_to_C

此外,对于指针类型,则是该咋用咋用,比如 *C.int 。但是对于 void * ,则需要用 unsafe.Pointer 来表示。

其他知识

如果说需要什么其他知识,那就是编译链接相关的知识了,推荐《程序员的自我修养-链接、装载与库》。这本书非常的好,国产神书, 不过说实话,因为不经常接触,看过然后又忘记了大部分内容 ‍♂️

CGO是如何运行的

在Go中调用C函数,cgo生成的代码调用 runtime.cgocall(_cgo_Cfunc_f, frame)_cgo_Cfunc_f 就是GCC编译 出来的代码。 runtime.cgocall 会调用 runtime.asmcgocall(_cgo_Cfunc_f, frame)

runtime.asmcgocall 会切换到 m->go 的栈然后执行代码,因为 g0 的栈是操作系统分配的栈(大小为8k),足够 执行C代码。 _cgo_Cfunc_f 在frame中执行C函数,然后返回到 runtime.asmcgocall 。之后再切回调用它的 G的栈。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning PHP, MySQL, JavaScript, and CSS

Learning PHP, MySQL, JavaScript, and CSS

Robin Nixon / O'Reilly Media / 2012-9-3 / USD 39.99

If you're familiar with HTML, you can quickly learn how to build interactive, data-driven websites with the powerful combination of PHP, MySQL, and JavaScript - the top technologies for creating moder......一起来看看 《Learning PHP, MySQL, JavaScript, and CSS》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具