1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00

fix typo in ch2.3

This commit is contained in:
lewgun 2018-01-09 14:08:19 +08:00 committed by GitHub
parent c638ab73dd
commit f67d178717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,7 +4,7 @@ Go是一门以实用为主要目的的编程语言我们可以通过cgo来直
## `import "C"`语句 ## `import "C"`语句
如果在Go代码中出现`import "C"`语句这表示使用了CGO特性在这行语句前面紧跟着的注释是一种特殊语法里面包含的是正常的C语言代码。当确保CGO启用的情况下还可以在当前目录中包含CC++对应的源文件。 如果在Go代码中出现`import "C"`语句则表示使用了CGO特性紧跟在这行语句前面的注释是一种特殊语法里面包含的是正常的C语言代码。当确保CGO启用的情况下还可以在当前目录中包含C/C++对应的源文件。
举个最简单的例子: 举个最简单的例子:
@ -27,11 +27,11 @@ func main() {
} }
``` ```
这个例子展示了cgo的基本使用方法。开头的注释中写了要调用的C函数和相关的头文件头文件被include之后里面的所有的C语言元素都会被加入到”C”这个虚拟的包中。需要注意的是import "C"语句前有特殊的注释语法,该导入语句需要单独一行不能与其他包一同import。向C函数传递参数也很简单就直接转化成对应C语言类型传递就可以。如上例中`C.int(v)`用于将强制类型转换将一个Go中的int类型值转化为C语言中的int类型然后调用C语言定义的printint函数进行打印。 这个例子展示了cgo的基本使用方法。开头的注释中写了要调用的C函数和相关的头文件头文件被include之后里面的所有的C语言元素都会被加入到”C”这个虚拟的包中。需要注意的是import "C"导入语句需要单独一行不能与其他包一同import。向C函数传递参数也很简单就直接转化成对应C语言类型传递就可以。如上例中`C.int(v)`用于将一个Go中的int类型值强制类型转换转化为C语言中的int类型然后调用C语言定义的printint函数进行打印。
需要注意的是Go是强类型语言所以cgo中传递的参数类型必须与声明的类型完全一致而且传递前必须用”C”中的转化函数转换成对应的C类型不能直接传入Go中类型的变量。同时通过虚拟的C包导入的C语言符号并不需要是大写字母开头它们不受Go语言的导出规则约束。 需要注意的是Go是强类型语言所以cgo中传递的参数类型必须与声明的类型完全一致而且传递前必须用”C”中的转化函数转换成对应的C类型不能直接传入Go中类型的变量。同时通过虚拟的C包导入的C语言符号并不需要是大写字母开头它们不受Go语言的导出规则约束。
cgo将当前包引用的C语言符号都放到了虚拟的C包同样其它依赖的Go语言包内部可能也是通过cgo引入了相似的虚拟C包但是不同的Go语言包引入的虚拟的C包之间的类型是不能通用的。这个问题约束看起来似乎没有什么影响但是对于自己要构造一些cgo辅助函数会有一点的影响。 cgo将当前包引用的C语言符号都放到了虚拟的C包同时当前包依赖的其它Go语言包内部可能也通过cgo引入了相似的虚拟C包但是不同的Go语言包引入的虚拟的C包之间的类型是不能通用的。这个约束对于要自己构造一些cgo辅助函数时有可能会造成一点的影响。
比如我们希望在Go中定义一个C语言字符指针对应的CChar类型然后增加一个GoString方法返回Go语言字符串 比如我们希望在Go中定义一个C语言字符指针对应的CChar类型然后增加一个GoString方法返回Go语言字符串
@ -65,7 +65,7 @@ func main() {
} }
``` ```
但是这段代码是不能正常工作的。因为当前main包引入的`C.cs`类型的变量是当前main包的cgo构造的虚拟的C包下的char类型指针它和cgo_helper包引入的`*C.char`类型是不同的。在Go语言中方法是依附于类型存在的不同Go包中引入的虚拟的C包却是不同的类型这导致从它们延伸出来的Go类型也是不同的类型这最终导致了前面代码不能正常工作。 这段代码是不能正常工作的因为当前main包引入的`C.cs`变量的类型是当前main包的cgo构造的虚拟的C包下的char类型它和cgo_helper包引入的`*C.char`类型是不同的。在Go语言中方法是依附于类型存在的不同Go包中引入的虚拟的C包的类型却是不同的这导致从它们延伸出来的Go类型也是不同的类型这最终导致了前面代码不能正常工作。
有Go语言使用经验的用户可能会建议参数转型后再传入。但是这个方法似乎也是不可行的因为`cgo_helper.PrintCString`的参数是它自身包引入的`*C.char`类型,在外部是无法直接获取这个类型的。换言之,一个包如果在公开的接口中直接使用了`*C.char`等类似的虚拟C包的类型其它的Go包是无法直接使用这些类型的除非这个Go包同时也提供了`*C.char`类型的构造函数。因为这些诸多因素如果想在go test环境直接测试这些cgo导出的类型也会于通用的限制。 有Go语言使用经验的用户可能会建议参数转型后再传入。但是这个方法似乎也是不可行的因为`cgo_helper.PrintCString`的参数是它自身包引入的`*C.char`类型,在外部是无法直接获取这个类型的。换言之,一个包如果在公开的接口中直接使用了`*C.char`等类似的虚拟C包的类型其它的Go包是无法直接使用这些类型的除非这个Go包同时也提供了`*C.char`类型的构造函数。因为这些诸多因素如果想在go test环境直接测试这些cgo导出的类型也会于通用的限制。
@ -73,7 +73,7 @@ func main() {
## `#cgo`语句 ## `#cgo`语句
`import "C"`语句前的注释中可以通过`#cgo`语句设置相关的编译阶段和链接阶段的参数。编译阶段的参数主要是定义相关宏和指定头文件检索路径。链接阶段的参数主要是指定库文件检索路径和要链接的库文件。 `import "C"`语句前的注释中可以通过`#cgo`语句设置编译阶段和链接阶段的相关参数。编译阶段的参数主要用于定义相关宏和指定头文件检索路径。链接阶段的参数主要是指定库文件检索路径和要链接的库文件。
```go ```go
// #cgo CFLAGS: -DPNG_DEBUG=1 -I./include // #cgo CFLAGS: -DPNG_DEBUG=1 -I./include
@ -85,7 +85,7 @@ import "C"
上面的代码中CFLAGS部分`-D`部分定义了宏PNG_DEBUG值为1`-I`定义了头文件包含的检索目录。LDFLAGS部分`-L`指定了链接时库文件检索目录,`-l`指定了链接时需要链接png库。 上面的代码中CFLAGS部分`-D`部分定义了宏PNG_DEBUG值为1`-I`定义了头文件包含的检索目录。LDFLAGS部分`-L`指定了链接时库文件检索目录,`-l`指定了链接时需要链接png库。
因为C/C++遗留的问题C头文件检索目录可以是相对目录但是库文件检索目录则需要绝对路径。如果库文件的检索目录中可以通过`${SRCDIR}`变量表示当前包目录的绝对路径: 因为C/C++遗留的问题C头文件检索目录可以是相对目录但是库文件检索目录则需要绝对路径。库文件的检索目录中可以通过`${SRCDIR}`变量表示当前包目录的绝对路径:
``` ```
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
@ -97,9 +97,9 @@ import "C"
// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
``` ```
`#cgo`语句主要影响CFLAGS、CPPFLAGS、CXXFLAGS、FFLAGS和LDFLAGS几个编译器环境变量。CFLAGS针对C语言代码设置编译参数。其中CFLAGS、CPPFLAGS、CXXFLAGS、FFLAGS几个变量用于改变编译阶段的构建参数LDFLAGS用于设置链接时的参数 `#cgo`语句主要影响CFLAGS、CPPFLAGS、CXXFLAGS、FFLAGS和LDFLAGS几个编译器环境变量。LDFLAGS用于设置链接时的参数除此之外的几个变量用于改变编译阶段的构建参数(CFLAGS用于针对C语言代码设置编译参数)
对于在cgo环境混合使用C和C++的用户来说可能有三种不同的编译选项其中CFLAGS对应C语言特有的编译选项、CXXFLAGS对应是C++特有的编译选项、CPPFLAGS则对应C和C++共有的便于选项。但是在链接阶段C和C++的链接选项是通用因此这个时候已经不在有C和C++语言的区别,它们都是相同的类型的目标文件 对于在cgo环境混合使用C和C++的用户来说可能有三种不同的编译选项其中CFLAGS对应C语言特有的编译选项、CXXFLAGS对应是C++特有的编译选项、CPPFLAGS则对应C和C++共有的编译选项。但是在链接阶段C和C++的链接选项是通用的因此这个时候已经不再有C和C++语言的区别,它们的目标文件的类型是相同的
`#cgo`指令还支持条件选择当满足某个操作系统或某个CPU架构类型时后面的编译或链接选项生效。比如下面是分别针对windows和非windows下平台的编译和链接选项 `#cgo`指令还支持条件选择当满足某个操作系统或某个CPU架构类型时后面的编译或链接选项生效。比如下面是分别针对windows和非windows下平台的编译和链接选项
@ -108,9 +108,9 @@ import "C"
// #cgo !windows LDFLAGS: -lm // #cgo !windows LDFLAGS: -lm
``` ```
其中在windows平台下编译前会预定义X86宏为1再非widnows平台下再链接阶段会要求链math数学库。这种用法对应在不太平台下只有少数编译选项差异的场景比较适用。 其中在windows平台下编译前会预定义X86宏为1再非widnows平台下在链接阶段会要求链接math数学库。这种用法对于在不同平台下只有少数编译选项差异的场景比较适用。
如果是在不同的系统下cgo对应者不同的c代码我们可以通过在`#cgo`指令定义不同的宏然后通过C语言的宏来区分不同的代码: 如果在不同的系统下cgo对应着不同的c代码我们可以先使用`#cgo`指令定义不同的C语言的宏然后通过宏来区分不同的代码:
```go ```go
package main package main
@ -141,7 +141,7 @@ func main() {
## build tag 条件编译 ## build tag 条件编译
build tag 是在Go或cgo环境下的C/C++文件开头的一种特殊的注释。条件编译类似签名的`#cgo`指令针对不同平台定义的宏,只有在对应平台的宏被定义之后才会构建对应的代码。但是通过`#cgo`指令定义宏有个限制它只能是基于Go语言支持的windows、darwin和linux等已经支持的操作系统。如果我们希望定义一个DEBUG标志的宏`#cgo`指令就无能为力了。而Go语言提供的build tag 条件编译特性则可以简单做到。 build tag 是在Go或cgo环境下的C/C++文件开头的一种特殊的注释。条件编译类似于前面通过`#cgo`指令针对不同平台定义的宏,只有在对应平台的宏被定义之后才会构建对应的代码。但是通过`#cgo`指令定义宏有个限制它只能是基于Go语言支持的windows、darwin和linux等已经支持的操作系统。如果我们希望定义一个DEBUG标志的宏`#cgo`指令就无能为力了。而Go语言提供的build tag 条件编译特性则可以简单做到。
比如下面的源文件只有在设置debug构建标志时才会被构建 比如下面的源文件只有在设置debug构建标志时才会被构建
@ -162,10 +162,10 @@ go build -tags="windows,debug"
我们可以通过`-tags`命令行参数同时指定多个build标志它们之间用逗号分割。 我们可以通过`-tags`命令行参数同时指定多个build标志它们之间用逗号分割。
当有多个build tag时我们将多个标志通过与或的规则来组合使用。比如以下的构建标志表示只有在linux/386或非cgo环境的darwin平台下才进行构建。 当有多个build tag时我们将多个标志通过逻辑操作的规则来组合使用。比如以下的构建标志表示只有在linux/386或非cgo环境的darwin平台下才进行构建。
```go ```go
// +build linux,386 darwin,!cgo // +build linux,386 darwin,!cgo
``` ```
其中`linux,386`中linux和386用逗号链接表示AND的意思`linux,386``darwin,!cgo`之间通过空白分割表示OR的意思。 其中`linux,386`中linux和386用逗号链接表示AND的意思`linux,386``darwin,!cgo`之间通过空白分割表示OR的意思。