menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right All_wiki chevron_right the-way-to-go_ZH_CN chevron_right eBook chevron_right 03.9.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    03.9.md
    3.83 KB / 2024-07-16 23:14:27
        # 3.9 与其它语言进行交互
    
    ## 3.9.1 与 C 进行交互
    
    工具 cgo 提供了对 FFI(外部函数接口)的支持,能够使用 Go 代码安全地调用 C 语言库,你可以访问 cgo 文档主页:[http://golang.org/cmd/cgo](http://golang.org/cmd/cgo)。cgo 会替代 Go 编译器来产生可以组合在同一个包中的 Go 和 C 代码。在实际开发中一般使用 cgo 创建单独的 C 代码包。
    
    如果你想要在你的 Go 程序中使用 cgo,则必须在单独的一行使用 `import "C"` 来导入,一般来说你可能还需要 `import "unsafe"`。
    
    然后,你可以在 `import "C"` 之前使用注释(单行或多行注释均可)的形式导入 C 语言库(甚至有效的 C 语言代码),它们之间没有空行,例如:
    
    	// #include <stdio.h>
    	// #include <stdlib.h>
    	import "C"
    
    名称 "C" 并不属于标准库的一部分,这只是 cgo 集成的一个特殊名称用于引用 C 的命名空间。在这个命名空间里所包含的 C 类型都可以被使用,例如 `C.uint`、`C.long` 等等,还有 libc 中的函数 `C.random()` 等也可以被调用。
    
    当你想要使用某个类型作为 C 中函数的参数时,必须将其转换为 C 中的类型,反之亦然,例如:
    
    ```go
    var i int
    C.uint(i) 		// 从 Go 中的 int 转换为 C 中的无符号 int
    int(C.random()) // 从 C 中 random() 函数返回的 long 转换为 Go 中的 int
    ```
    
    下面的 2 个 Go 函数 `Random()` 和 `Seed()` 分别调用了 C 中的 `C.random()` 和 `C.srandom()`。
    
    示例 3.2 [c1.go](examples/chapter_3/CandGo/c1.go)
    
    ```go
    package rand
    
    // #include <stdlib.h>
    import "C"
    
    func Random() int {
    	return int(C.random())
    }
    
    func Seed(i int) {
    	C.srandom(C.uint(i))
    }
    ```
    
    C 当中并没有明确的字符串类型,如果你想要将一个 `string` 类型的变量从 Go 转换到 C 时,可以使用 `C.CString(s)`;同样,可以使用 `C.GoString(cs)` 从 C 转换到 Go 中的 `string` 类型。
    
    Go 的内存管理机制无法管理通过 C 代码分配的内存。
    
    开发人员需要通过手动调用 `C.free` 来释放变量的内存:
    
    ```go
    defer C.free(unsafe.Pointer(Cvariable))
    ```
    
    这一行最好紧跟在使用 C 代码创建某个变量之后,这样就不会忘记释放内存了。下面的代码展示了如何使用 cgo 创建变量、使用并释放其内存:
    
    示例 3.3 [c2.go](examples/chapter_3/CandGo/c2.go)
    
    ```go
    package print
    
    // #include <stdio.h>
    // #include <stdlib.h>
    import "C"
    import "unsafe"
    
    func Print(s string) {
    	cs := C.CString(s)
    	defer C.free(unsafe.Pointer(cs))
    	C.fputs(cs, (*C.FILE)(C.stdout))
    }
    ```
    
    **构建 cgo 包**
    
    你可以在使用将会在第 9.5 节讲到的 Makefile 文件(因为我们使用了一个独立的包),除了使用变量 `GOFILES` 之外,还需要使用变量 `CGOFILES` 来列出需要使用 cgo 编译的文件列表。例如,示例 3.2 中的代码就可以使用包含以下内容的 `Makefile` 文件来编译,你可以使用 `gomake` 或 `make`:
    
    	include $(GOROOT)/src/Make.inc
    	TARG=rand
    	CGOFILES=\
    	c1.go\
    	include $(GOROOT)/src/Make.pkg
    
    # 3.9.2 与 C++ 进行交互
    
    SWIG(简化封装器和接口生成器)支持在 Linux 系统下使用 Go 代码调用 C 或者 C++ 代码。这里有一些使用 SWIG 的注意事项:
    
    - 编写需要封装的库的 SWIG 接口。
    - SWIG 会产生 C 的存根函数。
    - 这些库可以使用 cgo 来调用。
    - 相关的 Go 文件也可以被自动生成。
    
    这类接口支持方法重载、多重继承以及使用 Go 代码实现 C++ 的抽象类。
    
    目前使用 SWIG 存在的一个问题是它无法支持所有的 C++ 库,比如说它就无法解析 TObject.h。
    
    ## 链接
    
    - [目录](directory.md)
    - 上一节:[Go 性能说明](03.8.md)
    - 下一部分:[语言的核心结构与技术](04.1.md)
    
    
    links
    file_download