1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00
advanced-go-programming-book/ch3-asm/ch3-03-const-and-var.md
2018-06-03 17:46:00 +08:00

6.0 KiB
Raw Blame History

3.3. 常量和全局变量(Doing)

程序中的一切变量的初始值都直接或间接地依赖常量或常量表达式生成。在Go语言中很多变量是默认零值初始化的但是Go汇编中定义的变量最好还是手工通过常量初始化。有了常量之后就可以定义全局变量并使用常量组成的表达式初始化全部变量。本节将简单讨论Go汇编语言中常量和全局变量的用法。

常量

Go汇编语言中常量以$美元符号为前缀。常量的类型有整数常量、浮点数常量、字符常量和字符串常量等几种类型。以下是几种类型常量的例子:

$1           // 十进制
$0xf4f8fcff  // 十六进制
$1.5         // 浮点数
$'a'         // 字符
$"abcd"      // 字符串

其中整数类型常量默认是十进制格式,也可以用十六进制格式表示整数常量。所有的常量最终都必须和要初始化的变量内存大小匹配。

对于数值型常量,可以通过常量表达式构成新的常量:

$2+2      // 常量表达式
$3&1<<2   // == $4
$(3&1)<<2 // == $4

其中常量表达式中运算符的优先级和Go语言保持一致。

全局变量

在Go语言中变量根据作用域和生命周期有全局变量和局部变量之分。全局变量是包一级的变量全局变量一般有着较为固定的内存地址声明周期跨越整个程序运行时间。而局部变量一般是函数内定义的的变量只有在函数被执行的时间才能被创建当函数完成时将回回收暂时不考虑闭包对局部变量捕获的问题

从Go汇编语言角度来看局部变量和局部变量也大的差异。在Go汇编中全局变量和全局函数更为相似都是通过一个认为定义的符号来引用对应的内存区别只是内存中存放是数据还是要执行的指令。因为在冯诺伊曼系统结构的计算机中指令也是数据而且指令和数据存放在统一编址的内存中因此指令和数据并没有本质的差别——我们甚至可以像操作数据那样动态生成指令。而局部变量则需了解了汇编函数之后通过SP栈空间来隐式定义。

在Go汇编语言中内存是通过SB伪寄存器定位。SB是Static base pointer的缩写意为静态内存的开始地址。所有的静态全局符号通过可以通过SB加一个偏移量定位而我们定义的符号其实就是相对于SB内存开始地址偏移量。对于SB伪寄存器全局变量和全局函数的符号并没有任何区别。

要定义全局变量,首先要声明一个变量对应的符号,以及变量对应的内存大小。导出变量符号的语法如下:

GLOBL symbol(SB), width

GLOBL汇编指令用于定义名为symbol的变量变量对应的内存宽度为width内存宽度部分必须用常量初始化。下面的代码通过汇编定义一个int32类型的count变量

GLOBL ·count(SB),$4

其中符号·count以中点开头表示是当前包的变量,最终符号名为被展开为path/to/pkg.count。count变量的大小是4个字节常量必须以$美元符号开头。内存的宽度必须是2的指数倍编译器最终会保证变量的其实地址对齐到机器字宽度。需要注意的是在Go汇编中我们无法为count变量指定具体的类型。在汇编中定义全局变量时我们值关心变量的名字和内存大小变量最终的类型只能在Go语言中声明。

变量定义之后我们可以通过DATA汇编指令指定对应内存中的数据语法如下

DATA symbol+offset(SB)/width, value

具体的含义是从symbol+offset偏移量开始width宽度的内存用value常量对应的值初始化。DATA初始化内存时width必须是1、2、4、8几个宽度之一因为再大的内存无法一次性用一个uint64大小的值表示。

对于int32类型的count变量来说我们既可以逐个字节初始化也可以一次性初始化

DATA ·count+0(SB)/1,$1
DATA ·count+1(SB)/1,$2
DATA ·count+2(SB)/1,$3
DATA ·count+3(SB)/1,$4

// or

DATA ·count+0(SB)/4,$0x01020304

因为X86处理器是小端序因此用十六进制0x01020304初始化全部的4个字节和用1、2、3、4逐个初始化4个字节是一样的效果。

最后还需要在Go语言中声明对应的变量和C语言头文件声明变量的作用类似这样垃圾回收器会更加变量的类型来管理其中的指针相关的内存数据。

bool型变量

Go汇编语言定义变量无法指定类型信息因此需要先通过Go语言声明变量的类型。以下是在Go语言中声明的几个bool类型变量

var (
	boolValue  bool
	trueValue  bool
	falseValue bool
)

在Go语言中声明的变量不能含有初始化语句。然后下面是amd64环境的汇编定义

GLOBL ·boolValue(SB),$1   // 未初始化

GLOBL ·trueValue(SB),$1   // var trueValue = true
DATA ·trueValue(SB)/1,$1  // 非 0 均为 true

GLOBL ·falseValue(SB),$1  // var falseValue = true
DATA ·falseValue(SB)/1,$0

bool类型的内存大小为1个字节。并且汇编中定义的变量需要手工指定初始化值否则将可能导致产生未初始化的变量。

int型变量

所有的整数类型均有类似的定义的方式比较大的差异是整数类型的内存大学和整数是否是有符号。下面是声明的int32和uint32类型变量

var int32Value int32

var uint32Value uint32

在Go语言中声明的变量不能含有初始化语句。然后下面是amd64环境的汇编定义

GLOBL ·int32Value(SB),$4
DATA ·int32Value+0(SB)/1,$0x01  // 第0字节
DATA ·int32Value+1(SB)/1,$0x02  // 第1字节
DATA ·int32Value+2(SB)/2,$0x03  // 第3-4字节

GLOBL ·uint32Value(SB),$4
DATA ·uint32Value(SB)/4,$0x01020304 // 第1-4字节

汇编定义变量时并不区分整数是否有符号。

float型变量

TODO

string类型变量

TODO

slice类型变量

TODO

map/channel类型变量

TODO

标识符规则和特殊标志

TODO