cgo 提供了 golang 和 C 语言相互调用的机制

cgo使用需要安装gcc

1
2
3
4
5
6
> gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

通过import "C"语句启用cgo特性

示例

可以用注释符//和/**/包围C代码,

import "C" 和包含C代码之间没有空行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// +build go1.10
package main

//void SayHello(_GoString_ s);
import "C"

import (
    "fmt"
)

func main() {
    C.SayHello("Hello, World\n")
}

//export SayHello
func SayHello(s string) {
    fmt.Print(s)
}

使用cgo声明一个函数void SayHello,在go程序内实现这个函数,使用export关键字输出符号为SayHello供c程序调用

运行输出:

1
2
> go run sayhello.go
Hello, World

引入c语言标准库,在go程序中调用c实现的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

/*
#include <stdio.h>
#include <errno.h>

static void SayHello(const char* s) {
    puts(s);
}

void printint(int v) {
    printf("printint: %d\n", v);
}

static int add(int a, int b) {
    return a + b;
}

static int div(int a, int b) {
    if (b == 0) {
        errno = EINVAL;
        return 0;
    }
    return a/b;
}
*/
import "C"
import (
    "fmt"
    "os"
)

func main() {
    C.SayHello(C.CString("Hello, World\n"))
    C.printint(C.int(10))
    fmt.Println(os.Getenv("CGO_ENABLED"))
    fmt.Println(os.Getenv("GOPATH"))
    fmt.Println(C.add(6, 4))
    v1, err1 := C.div(6, 2)
    fmt.Println(v1, err1)
    v2, err2 := C.div(6, 0)
    fmt.Println(v2, err2)
}

运行输出:

1
2
3
4
5
6
7
8
9
> go run hello.go
Hello, World

printint: 10

/Users/piao/data/golang
10
3 <nil>
0 invalid argument

运行go tool cgo hello.go命令,可生成了一个包含c目标文件的文件夹_obj

引入c语言标准库,在go程序中调用c标准库函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

/*
#include <stdlib.h>
*/
import "C"
import (
    "fmt"
    "time"
)

func main() {
    C.srandom(C.uint(time.Now().UTC().UnixNano()))
    for i := 0; i < 10; i++ {
        fmt.Printf("%d\n", int(C.random()))
    }
}

运行输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> go run main.go
1127749496
1069904962
1041856883
569707171
457553066
1182433128
1819607608
295108270
1821523696
1298408739

独立c语言文件

1
2
3
4
5
#include <stdio.h>

void SayHi(const char* s) {
    puts(s);
}
1
2
3
4
5
6
7
8
package main

//void SayHi(const char* s);
import "C"

func main() {
    C.SayHi(C.CString("Hello, cgo"))
}

使用go run .go build . 命令编译

参考