参考: 1.6.3
Source code representation
- 源码文件使用
UTF-8
文件存放.
Lexical elements
Comments
- 与 C++ 一致.
Tokens
- Token 分为四类:identifiers, keywords, operators and delimiters, and literals
- White space(参考原文.哪些字符被认为是 White space),在不需要的时候会被忽略.
- Next Token;定义.
Semicolons
-
The formal grammar uses semicolons “;” as terminators in a number of productions. 这点与 C++ 一致,使用’;’作为终止符.
-
’;’ 在一些情况下可以不用加,这点与 C++ 不一致, C++ 少了一个’;’都编译不过去~.具体哪些情况下可能不加,如下:
- line’s final token(最后一个 token) 是以下几种情况.
-
To allow complex statements to occupy a single line, a semicolon may be omitted before a closing “)” or “}”,即:
auto x = [] () { return 33 ; } // 在 C++ 中,33 之后的';'不可缺少;但是在 Go 中,可以没有这个';'.
Identifiers
- 标识符规则与 C++ 中一致.
Keywords
- 就是一些关键词咯
Operators and Delimiters
- 本来还纳闷 delimiter 是个什么东西,现在看来,大概就是’}’这些.
Integer literals
- Integer literals 用来表示 integer constant(关于什么是 integer constant,参考下文).
- Integer literals 有以下几种写法,见原文,就是 10 进制,8 进制,16 进制(居然没有二进制);
Floating-point literals
- 表示 floating-point constant.
- 几种写法参考原文.
Imaginary literals
未看
Rune literals
- Rune 类型;首先要知道 rune 类型,也就是 integer value identifying a Unicode code point.
- rune literal 用来表示 rune constant.
-
rune 的写法:expressed as one or more characters enclosed in single quotes, as in ‘x’ or ‘\n’. Within the quotes, any character may appear except newline and unescaped single quote.具体:
- A single quoted character represents the Unicode value of the character itself.
\x
followed by exactly two hexadecimal digits;\u
followed by exactly four hexadecimal digits;\U
followed by exactly eight hexadecimal digits, and a plain backslash\
followed by exactly three octal digits. In each case the value of the literal is the value represented by the digits in the corresponding base.- 还有一些
\
与单个字母组成的转义,如:\n
,具体详见原文.
String literals
- 表示 string constant
-
string literals 分为 raw string literal, interpreted string literals.
- raw string literal 的语法,以及其标识的字符串值是多少.
- interpreted string literals 的语法,以及其标识的字符串值是多少.
Constants
-
constants,这里是指编译期常量,有很多场合可以产生编译期常量,具体参见原文.不过有几个特殊的 constants 类型这里介绍一下:
true
,false
;其实主要是想介绍他们是预先定义的常量,而不是关键字.-
Numeric constants;这里主要是想说:Numeric constants represent exact values of arbitrary precision and do not overflow.大概使用了类似 gmp 的方式吧.
Q1:原话如下:
Numeric constants represent exact values of arbitrary precision and do not overflow. Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, and not-a-number values.
其实不是很理解,这个 Consequently(因此) 为啥这些因此;not-a-number,infinity 在 IEEE-754 中 是一个特殊编码,应该不因为 numeric constants 采用任意精度就不存在了吧.
-
typed,untyped;Constants may be typed or untyped.
-
哪些 constants 是无类型的,见原文;这里比较震惊的是 literal 居然是无类型的.
-
A constant may be given a type explicitly balabalaxxx,这里可以参考原文;
-
default type;An untyped constant has a default type;如下:
i := 33 // 这里 literal 33 是无类型的,但是很显然 i 需要一个类型,所以这里就得用到 default type.
这里主要注意 default type 的使用场景,见原文. 第二就是某些 untyped numeric constant 其 default type 是个啥.
-
Variables
-
storage location;Variable 是一个 storage location.唉,为啥这里要说是 storage location,我老觉得 location 表明 variable 其实只是一个引用,就跟 JAVA 一样.
-
composite literal;只是想说明一下 composite literal 是 newly allocated variable,按照我的理解, go 语言中对 composite literal 的处理大概是先
new
分配一块内存,然后按照 literal 的内容来初始化 这块内存.并不像 C++ 中.如下:struct X { int x; int y; }; int main(int ,char **) { const X *ptr = &X{3,3}; // error: taking address of temporary return 0; }
- Structured variables;只是想说
map
居然不是 Structured variables,也就是&map[key]
取值就是不合法的咯. -
static type,dynamic type;任何 variables 都有 static type;当 variables 的 static type 是
interface
时,其 同时也有个 dynamic type.具体参见原文. - zero value;若 variables 从来没有被赋值过,则其值为其 static type 的 zero value.
Type
-
named type, unnamed type;
- named type;由标识符来指定;如:
int
.另外int
,string
,int32
这些只是预先定义的标识符,并不是关键词. -
unnamed type;通过
type literal
来指定,如下:var x struct { i1, i2 int }; // 此时 x 的类型就是 unnamed type.
注意看一下原文,确定 type literal 包括了哪些.
composite type;注意看一下原文指定了哪些 type 是 composite type.
- named type;由标识符来指定;如:
-
underlying type;Each type T has an underlying type,注意看一下原文中关于 underlying type 的定义.
Method sets
-
任何 type 都有 method set;注意是任何.下面介绍不同 type 具有的 method set 是啥样的:
-
若 type 的 underlying type 是
interface
,那么 type 的 method set 就是interface
声明时定义的 接口集合. -
若
T
是 named type,并且其 underlying type 不是指针类型,那么:- 类型
T
的 method set 是 balabala,参考原文. - 类型
T*
的 method set 是 balabala,参考原文.
func (this **int) g() { } // error;因为 *int 是 type literal,是 unnamed type. type PI *int func (this PI) g() { } // error: 因为 PI 是指针类型,不能用作 receiver.
- 类型
-
其他情况下,type 的 method set 为空.注意是空,并不是没有 method set,method set 是每一个 type 必有的.
-
-
method set 有啥意义;
- 类型 type 的 method set 可以表明 type 实现了哪些
interface
. - 类型 type 的 method set 表明 type 作用用作哪些 method 的 receiver. 这里说法感觉不是很精确.参见 ‘Go tour/method’ 一节.
- 类型 type 的 method set 可以表明 type 实现了哪些
Boolean types
没啥要说的
Numeric types
这个也是,没啥要说的
String types
string
大概就是字节数组了;Strings are immutable: once created, it is impossible to change the contents of a string.len(str)
用来获取str
的字节长度.str[0],str[idx]
用来取str
相应下标字节值.
Array types
[n]T
是数组类型,这里n
是编译期常量.len(array)
获取数组的长度.array[idx]
获取数组指定下标处的元素-
数组类型变量是值,也即其拷贝或者复制时是逐元素拷贝,如下:
a1 := [2]int {3,2} a2 := [2]int {3,2} a1 = a2 // 这里会 a1[0] = a2[0],a1[1] = a2[1] a3 := a2 // 这里也会 a3[0] = a2[0],a3[1] = a2[1]
Slice
这里介绍的东西与 tour 一致,去看 ‘go tour’
Struct types
-
struct
是 field 的集合.field 具有 name,type 俩属性.参见原文对 struct 的语法描述.anonymous field(别称:embedded field,an embedding of the type),即只是指定了 type 的 field,未指定 field 的 name, The unqualified type name(未加任何限定的 type name) acts as the field name.如下:
struct { T1 // field name is T1 *T2 // field name is T2 P.T3 // field name is T3 *P.T4 // field name is T4;未加任何限定的意思,去除包名,指针. x, y int // field names are x and y }
anonymous field 的 type 其形式参见原文中的语法;这里
TypeName
是 named type,而且其 underlying type 不能是 指针类型;当 anonymous field 的 type 是*Typename
类型时,Typename
underlying type 也不能是interface
类型. -
promoted field or method;参见原文中的定义
- Promoted fields;有啥语义,参见原文.
-
Promoted methods;Given a struct type S and a type named T, promoted methods are included in the method set of the struct. 具体规则参见原文.
Q1: 原文中描述,当
S
包含着类型为T
的 anonymous field 时,S
的 method set 包括了 methods with receiverT
. 也即没说’包括了 methods with receiver*T
‘,但是试了一下,可以发现S
对象可以调用 methods with receiver*T
;如:package main import "fmt" type T struct { i int } func (this T) f() { fmt.Println(this.i) } func (this *T) g() { fmt.Println(this.i,&this.i) } type IBase interface { g() } type S struct { T } func main() { var s S s.g() // 可以调用 p := &s var base IBase = p // 可以赋值 base.g() } // 以上行为表明:当 `S` 包含着类型为 `T` 的 anonymous field 时,`S` 的 method set 包括了 methods with receiver `T` or `*T`.
-
tag; 参见原文知其定义,以及有啥用处.
Pointer types
- base type;了解这个概念就好
- 参见 ‘go tour/pointer’.
Function types
-
A function type variable 可以取 the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil. function type 的语法参见原文.
注意 Signature 的概念.
Q1:
func (a,b int,)
与func (a,b int)
有啥区别嘛?Q2:根据语法中,Result 是可以省略的,省略了会咋样,表明函数不返回任何返回值么?
-
variadic function,即函数参数列表中最后一个参数 type 之前使用了
...
前缀,此时可以 invoked with zero or more arguments for that parameter(指最后一个 parameter).
Interface Type
-
interface type,其 zero value 是
nil
;interface Type 类型的变量可以存放着任何T
类型的值, 只要T
类型的 method set 是 interface type 定义 interfaces 的超集,此时又称T
实现了 interface type.注意
interface {}
类型可以存放任何类型的值.interface type 语法,参见原文.
-
embedding interface
E
inT
;此时表明 adds all (exported and non-exported) methods ofE
to the interfaceT
. 此时E
必须是 named interface type. 如下:type I interface { f() interface { g() } // error: embedding interface 必须是 named. }
Map type
- 类似于 C++ 中的
std::unordered_map
,也就是说是无序的. -
对键值类型的要求;The comparison operators
==
and!=
must be fully defined for operands of the key type. If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic.Q1: 但是,如下并没有触发 run-time panic,难道是编译器默认生成了
operator ==
,operator !=
操作?package main import "fmt" type S struct {} func main() { m := map[interface{}]int {} i := S{} j := S{} m[i] = 33 m[j] = 77 fmt.Println(m) }
Q2: 哈希函数难道不需要用户手动提供么?
- 创建,以及增删查改操作,具体参考原文.
Channel types
-
channel type 就是一个 first-in-first-out,生产者-消费者队列.其 type literal 语法参见 原文;
注意
<-
指定了 channel type 的方向,send or receive,具体如何参见原文.A channel may be constrained only to send or only to receive by conversion or assignment. 如下:var s chan int var is chan<- int = s // 大概意思是这样的.
The <- operator associates with the leftmost chan possible(这就是一个语法细节了).
The value of an uninitialized channel is
nil
. -
make(chan)
,具体参考原文;这里注意一下当构造一个 unbuffered chan 时,send 方的行为.本来不是很理解,后来看到一篇文章才感觉清晰, 就是 send 方在发送完数据之后要一直阻塞,直至数据已经被 receive 方取走.
-
A single channel may be used in send statements, receive operations, and calls to the built-in functions
cap
andlen
by any number of goroutines without further synchronization
Properties of types and values
Type identity
-
Two types are either identical or different,具体如何判断参考原文.
主要就是’Two named types are identical if their type names originate in the same
TypeSpec
.’如何理解? 首先在TypeSpec
中,只允许出现一个 type name,如下:type i32 int32 type i64,i int64 // error,不允许出现俩
这样的话,originate in the same
TypeSpec
大概就是指两个 named type 实际上是一样的类型,如下原文中一句话:T0
andT0
are identical.T0
andT1
are different because they are named types with distinct declarations.这里关于
T0
,T1
是啥,参考原文.
Assignability
- 讲述了何时
x
assignable toT
,这里x
是一个值,T
是一个类型,x
assignable toT
意味着可以将x
赋值 (或者初始化)T
类型变量.参考原文看看何时x
assignable toT
.
Blocks
- Blocks 语法,首先参考原文中语法了解 Block 是啥
-
了解除了明确定义的 Block 之外,还有哪些隐式定义的 Block.
Each “if”, “for”, and “switch” statement is considered to be in its own implicit block. 这句话啥意思呢, 如下:
package main import ( "fmt" ) func X() (int, int) { return 3, 7 } func main() { if a, b := X(); a < 2 { // 1 fmt.Println(a, b) } else if b := 9; b < 8 { // 2 fmt.Println(a, b) } else { fmt.Println(a, b) } // 3 }
1 处的
if
定义的 block 从 1 到 3; 2 处的if
定义的 block 是从 2 到 3. 1 处 if 的 block 包含了 2 处 if 的 block.
Declarations and scope
-
Declarations 将 non-blank identifier 与 entity 绑定在一起,这里 entity 可能是 constant, type, variable, function, label, or package. 不过这里有如下几个意外:
-
当 blank identifier 出现在 Declarations, 此时 does not introduce a binding, 当由于语法不得不声明声明一个变量, 但是本身又不需要这个变量时, 可以使用 blank identifier,如下:
func f(x, y int) (a, b int) { } a1, _ := f(int1, int2); // 这里只需要返回值 a, 不需要 b, 但是语法要求必须有个标识符, 所以使用了 _.
-
In the package block, the identifier
init
only be used for init function declarations, and it does not introduce a new binding.
does not introduce a new binding, 也就是这个标识符并没有与任何一个 entity 绑定在一起, 也就是说使用这个标识符会 提示标识符未定义, 如下:
package main func init() { } func main() { init() // error: undefined: init }
语法; Declarations 的语法. 参见原文, 注意 top level 这个概念.
-
-
作用域; 这里的作用域与 C++ 中概念一致, 参见原文, 参见标识符作用域规则.
需要注意地就是: 这里
import
居然是个 import declaration, 那就是引入的标识符与一个 package 绑定在一起咯.The
package
clause is not a declaration; the package name does not appear in any scope. Its purpose is to identify the files belonging to the same package and to specify the default package name for import declarations.
Label scopes
这里只是介绍了 label 的作用域, 关于 label 声明的语法以及语义参考原文相应章节,
-
The scope of a label is the body of the function in which it is declared and excludes the body of any nested function. labels do not conflict with identifiers that are not labels. 如下:
func main() { test: test := 33; fmt.Println(test); goto test; return ; }
Blank identifier
-
The blank identifier is represented by the underscore character
_
. It serves as an anonymous placeholder instead of a regular (non-blank) identifier.blank identifier has special meaning in declarations, as an operand, and in assignments. 起码到此为止, 知道了 blank identifier 在 declaration 中有什么 special meaning 了.
Predeclared identifiers
就是一些预先定义的标识符咯, int
居然只是一个预先定义的标识符 @_@.
Exported identifiers
- exported 的语义, An identifier may be exported to permit access to it from another package.
-
exported 规则, 用于确定一个 identifier 是否是 exported 的, 参见原文.
Q1: 这里不是很理解的就是标准指定了如下规则用来判断一个 identifier 是否是 exported 的:
- 当 identifier 首字母大写, 并且 identifier 在 package block 中声明.
- 当 identifier 首字母大写, 并且 identifier 是一个 method name.
按照 TopLevelDecl 的定义(这里参见原文), method name 应该只能在 package block 中声明吧,所以这俩条规则 是不是重复了.
Uniqueness of identifiers
我觉得没讲啥有用的东西
Constant declarations
-
constant 的语法. 参见原文.
别忘了, 这里 constant 可能是 typed, 也可能是 untyped.
If the type is omitted, the constants take the individual types of the corresponding expressions. 也就是 说可以在一条 ConstSpec 中声明多个类型不一致的常量(本来我以为只能声明同类型的常量呢), 如下:
const i, j = int(33), float64(7.7);
-
Within a parenthesized
const
declaration list, Such an empty expression list is equivalent to the textual substitution of the first preceding(first preceding, 可以理解为上一个) expression list and its type if any. 如下:const ( kI int = 33 kj // 这里等同于: kj int = 33 kt // 这里等同于: kj int = 33 )
而且我很好奇地试了一试下面这个:
const ( kI int = 33 kj kt float64 // error: const declaration cannot have type without expression, 我还以为等同于 kt float64 = float64(33) 呢. )
iota
-
the predeclared identifier
iota
represents untyped integer constants.It is reset to 0 whenever the reserved word
const
appears in the source and increments after each ConstSpec. 如下:const ( bit0, mask0 = 1 << iota, 1 << iota - 1 // bit0 == 1, mask0 == 0 // 这里 iota 只会在一条 ConstSpec 之后才会递增, 所以这里 iota 一直是 0. // BTW, 我本来以为 1 << iota -1 是: 1 << (iota - 1), 没想到是: (1 << iota) - 1. 啧啧 bit1, mask1 // bit1 == 2, mask1 == 1 _, _ // skips iota == 2 bit3, mask3 // bit3 == 8, mask3 == 7 )
Type declarations
-
A type declaration binds an identifier, to a new type, 注意参见原文的语法.
-
new type 与 existing type 具有相同的 underlying type, operations defined for the existing type are also defined for the new type. 其实这里应该可以证明, 当 Type A, Type B 具有相同的 underlying type 时, 那么 operations defined for A(or B) are also defined for B(or A).
-
The new type is different from the existing type. 意味着相互之间不能互相赋值.
-
关于 method set, 若 existing type 是 interface 类型, 则 new type 与 existing type 具有相同的 method set. 否则:The declared type does not inherit any methods bound to the existing type.
the method set of elements of a composite type remains unchanged. 如下:
type Mutex struct { /* Mutex fields */ } func (m *Mutex) Lock() { /* Lock implementation */ } func (m *Mutex) Unlock() { /* Unlock implementation */ } // The method set of *PrintableMutex contains the methods // Lock and Unlock bound to its anonymous field Mutex. type PrintableMutex struct { Mutex } // 此时 *PrintableMutex 的 method set 就有 Lock(), Unlock().
Variable declarations
-
variable declaration 将 identifiers 绑定到 variables 上, 每一个 variable 都有 a type and an initial value.
-
关于 variable 的 init value, If a list of expressions is given, the variables are initialized with the expressions following the rules for assignments. Otherwise, each variable is initialized to its zero value.
-
关于 variable 的 type, If a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first converted to its default type;
也就是与 constant 不同, variable 必须具有类型.
Short variable declarations
-
参见原文, 看其语法, 并了解 short variable declaration 就是
var
declaration 的一种简写. -
与
var
declaration 不同, 在 short variable declaration 中可以为一个已经存在的变量赋值, 不过此时要满足 一下条件:- originally declared earlier in the same block, 若 block 是 function body, 则也可以是 the parameter lists.
- at least one of the non-blank variables is new.
如下:
i := 33 if true { i, j := 77, 22 // 这里 i 是一个新声明的变量, 而不是赋值, 因为外部的 i 并不在同一个 block 中. }
Function declarations
-
参考原文了解其语法.
A function declaration may omit the body. 此时只是一个声明, 函数的实现放在 go 语言之外实现, 比如 放在 C 库中实现. 若函数的实现也是在 go 语言之中, 则不需要声明.
If the function’s signature declares result parameters, the function body’s statement list must end in a terminating statement.
Method declarations
-
参考原文, 了解其语法.
receiver, receiver base type 的概念.
The type of a method is the type of a function with the receiver as first argument. 但 a function declared this way is not a method. method 居然还有类型, 而且是个这.
the method name is visible only within selectors for type
T
or*T
. 如下:func (i I) fff() { } func main() { var i I i.fff() fmt.Printf("%T\n", fff); // undefined: fff return ; }
Expression
Operands
-
Operands denote the elementary values in an expression.
然后开始介绍一些种类的 operand.
Qualified identifiers
-
A qualified identifier is an identifier qualified with a package name prefix.
用来 accesses an identifier in a different package.
Composite literals
-
语义: Composite literals construct values for structs, arrays, slices, and maps(并且当且仅支持这些类型).
Create a new value each time they are evaluated. Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal’s value.
-
语法: 首先要了解 composite literal 总体的语法格式, 然后再了解当用于为 struct, array, slice, maps 指定值时具体的语法结构.
当用于 struct 时, 此时 Key 表示 struct field name; 并且若 ElementList 中有一个 KeyedElement 具有 Key, 那么所有 KeyedElement 都必须具有 Key; 反之则是所有的 KeyedElement 都没有 Key, 此时表明按照 struct 中 field 声明的顺序来为 field 指定值. 若使用了 Key, 则为指定的元素赋值.
当用于 array, slice 时, 此时 Key 表示 marking its position in the array(或 slice), 具体规则参考原文. 当用于 array 时,
[...]
表示数组的长度由编译器在编译时计算. 当用于 slice 时, slice 的 capacity 等于 slice.length.对于 array, slice, map 的 composite literal, 其内的 composite literal 在某些情况下可以省略 literal type, 具体参考原文.
当 composite literal 出现在
if
,for
,switch
以及其关联 block 的{
之间时, 可能会有解析二义性 的问题, 具体参考原文.
Function literals
-
function literal represents an anonymous function, can be assigned to a variable or invoked directly.
-
closures, 闭包, 按照我的理解, 闭包的定义是: 闭包是一个 function, 并且 refer to variables defined in a surrounding function.
Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.
func f () (func (), func ()) { i := 33; f1 := func () { fmt.Println(i) i -= 1 fmt.Println(i) } f2 := func () { fmt.Println(i) i -= 1 fmt.Println(i) } f1() f2() return f1, f2 } func main() { f1, f2 := f() f1() f2() return ; } // 演示了 shared 的语义.
Primary expressions
- 知晓这个概念即可.
Selectors
- selector expression, selector 概念.
- depth in T 概念.
- 关于 selector 的语法规则, 参考原文.
Method expressions
-
If
M
is in the method set of typeT
,T.M
is a function that is callable as a regular function with the same arguments asM
prefixed by an additional argument that is the receiver of the method.参见原文, 有个栗子, 如:
f := T.Mv; // f 的原型: func(tv T, a int) int f := (*T).Mp; // f 的原型: func(tp *T, f float32) float32 // f 的原型: func(tv *T, a int) int, 注意这里. Mv 也是 *T 的 method set 之一. 此时会 // through the receiver to create a value to pass as the receiver to the underlying method; f := (*T).Mv;
It is legal to derive a function value from a method of an interface type. The resulting function takes an explicit receiver of that interface type. 就是说, 上面
T
也可以是 interface type.
Method values
-
method value 的概念, 参见原文.
method value 也是 function value, 其调用形式参见原文.
method value 的求值, The expression x is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later.
The type T may be an interface or non-interface type. 注意这里当是 interface 时, 有一处行为不一致, 参考: https://www.zhihu.com/question/49926291
-
几处语法问题:
pt.Mv
is equivalent to(*pt).Mv
.t.Mp
is equivalent to(&t).Mp
.
这里具体参见原文.
Index expressions
- 参见原文即可. 并没有啥.
-
map[key]
出现 an assignment or initialization 时, 有种特殊形式.Q1: 当
map[key]
出现在 assigment 的左侧时, 若key
已经存在, 则没有啥问题, 此时map[key]
表示在map
中被key
索引的元素, 这里相当于更改这个元素的值. 但是当key
不存在时, 为什么这里会有 插入语义? 没看到标准在哪里指定了这种情况.
Slice expressions
-
语义: Slice expressions construct a substring or slice from a string, array, pointer to array, or slice.
There are two variants: simple form, full form.
-
simple form;
oprand[low, high]
.-
oprand; 可以是 string, array, pointer to array, slice; 当是 pointer to array, 等同于 array.
-
low, high; 若 low 省略, 则取值为 0; 若 high 省略, 则取值为
len(oprand)
. 当 oprand 不同时, low, high 具有如下范围:- string, array; 0 <= low <= high <= len(oprand).
- slice; 0 <= low <= high <= cap(oprand).
-
result; 当 oprand 是 string(包括 untyped string) 时, 结果是 non-constant string 类型. 其他情况下, 结果都是 slice 类型. 这里具体可以参考原文.
the result shares its underlying array with the operand.
-
-
full form; 与 simple form 相比, 只是多了个指定 result capacity 的功能. 具体参考原文.
Type assertions
- Type assertions, 其语法以及规则, 参见原文
- Type assertion special form, 参见原文. 注意此时引入个 untyped boolean value.
Calls
-
function call, method call; 主要讲了其参数可以是 0 个或多个 singe-valued expression, 也可以是一个 multi-valued function call. 具体参考原文.
function call 的执行顺序, 具体参考原文, 只要注意无论是参数还是返回值, 都是传值.
-
可变参数函数; 具体参考原文, 要注意:
- 函数内部对变参的处理, 当作 slice 类型来使用.
- 调用可变参数函数时, 不传递参数, 传递多个参数分别意味着什么; 使用 slice 来原址传参.
Operators
可以认为本章以上部分都只是了介绍了各种姿势的 oprand, 现在开始介绍 operators, operators 将 oprand 组合在一起形成表达式.
-
首先讲了一堆规则, 这些规则梳理之后如下(具体还是参考原文的好):
Comparisons are discussed elsewhere. constant expressions 也是另外讨论.
除了 Comparison operator, the operand types must be identical unless the operation involves shifts or untyped constants.
除了 comparison operator, Except for shift operations, if one operand is an untyped constant and the other operand is not, the constant is converted to the type of the other operand.
对于 shift operator, 其右侧运算符必须要遵守的规则: must have unsigned integer type or be an untyped constant that can be converted to unsigned integer type; 其左侧运算符必须要遵守的规则: if the left operand of a non-constant shift expression is an untyped constant, it is first converted to the type it would assume if the shift expression were replaced by its left operand alone(即假设此时 shift operation 整个被 left operand 替换, 这时候因为 left operand 是 untyped constant, 所以会被转化. 这个转化规则就是原 shift operation 中 left operand 的转化规则). 如下:
var i = 1<<s // 1 has type int, 因为这里 var i = 1 << s --> var i = 1, 此时 1 会首先转化为 default type(int)
-
Operator precedence; 一元运算符具有最高的优先级(
++
,--
是 statement, 不是运算符!), 二元运算符的优先级参考原文.Binary operators of the same precedence associate from left to right.
Arithmetic operators
- 注意 Arithmetic operators 有几个; 以及他们 yield a result of the same type as the first operand.
Integer operators
-
integer values
x
andy
,x / y
,x % y
的规则. 注意有一个很诡异的 exception(例外). 注意当y
为0
时会 panic. -
shift operators 的几条规则:
-
They implement arithmetic shifts if the left operand is a signed integer and logical shifts if it is an unsigned integer.
-
There is no upper limit on the shift count. Shifts behave as if the left operand is shifted
n
times by1
for a shift count ofn
. -
x << 1
is the same asx * 2
andx >> 1
is the same asx/2
but truncated towards negative infinity. 注意正常的x / 2
是 truncated towards zero.
-
-
unary operators
+
,-
, and^
are defined as 原文. 这里参见原文.
Integer overflow
- 只是讲了无符号整数溢出了咋办, 有符号整数溢出了咋办; 这里对溢出的处理很像 C++. 具体参考原文.
Floating-point operators
+x
,-x
规则.- division by zero, whether a run-time panic occurs is implementation-specific.
String concatenation
-
字符串拼接, 通过
+
运算符,+=
assign statement.String addition creates a new string by concatenating the operands. 因为 string 一经创建便不再被修改.
Comparison operators
-
comparison 的语义都很清楚了; 这里主要讲述一下 comparison 的语法.
-
yield an untyped boolean value; 按照我的理解, comparison oprand 在完成比较之后, 会生成
true
,false
这俩个 untyped boolean constant. -
operand 的要求;
-
comparable, ordered 的概念.
-
comparison 的规则, 一绷子. 参加原文. 这里讲述两个比较特殊的:
-
当 comparison 的 operand 既不是 comparable, 也不是 ordered 时, 会导致 runtime-panic, 这个规则具体可以参考原文.
-
任何类型总是可以与
nil
进行比较. 当然这个规则具体可以参考原文.
-
-
Logical operators
这里, 就讲述了两条规则:
- Logical operators apply to boolean values and yield a result of the same type as the operands.
- The right operand is evaluated conditionally.
Address operators
-
&
运算符; 作用的 operand; 以及表达式的结果. 同时也讲述了 operand 的要求:- addressable, 关于 adddressable 具体是指什么, 我觉得了解一下就行了, 让编译器帮我们识别就好.
- composite literal.
-
*
运算符; 作用的 operand; 以及表达式的结果.
Receive operator
-
首先, 注意了解 operand 的类型, 以及 receiver operation 结果的类型.
第二个了解, receiver operation 的规则, 大致总结如下:
- Receiving from a
nil
channel blocks forever. - The expression blocks until a value is available.
- A receive operation on a closed channel can always proceed immediately, yielding the element type’s zero value after any previously sent values have been received.
第三个了解 receiver operation 的 special form.
- Receiving from a
Conversions
-
首先, 讲述了 conversion expression 的语法
T(x)
, 注意当T
为某些类型时, 可能会让 编译器解析二义性, 这点具体可以参考原文.当
x
为 constant value 时,T(x)
应该遵守的三条规则, 此时T(x)
的结果是 typed constant.当
x
为 non-constant value 时,T(x)
应该遵守的七条规则, 此时注意:- There is no linguistic mechanism to convert between pointers and integers. The package
unsafe
implements this functionality under restricted circumstances.
Q1: Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. 这段没有看懂.
- There is no linguistic mechanism to convert between pointers and integers. The package
Conversions between numeric types
讲述了在 T(x)
中, 当 x
为整数/浮点数, T
也为整数, 浮点数时转化规则. 可以分为: 整数到
整数的转化规则, 整数到浮点数的转化规则, 浮点数到整数的转化规则, 浮点数到浮点数的转化规则.
- 整数到整数, 参见原文.
- 浮点数到整数, 参见原文.
-
整数, 浮点数到浮点数; 参见原文.
Q1: the value of a variable
x
of typefloat32
may be stored using additional precision beyond that of an IEEE-754 32-bit, 这种有可能么?
In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.
Conversions to and from a string type
参见原文就行.
Constant expressions
-
Constant expression 的概念; Constant expressions may contain only constant operands and are evaluated at compile time. Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language. 按照我的理解, constant expression 总是被精确求值, 就是 GMP 库支持的那种精确求值, 也即运算的中间结果以及 constant operand 本身的精度可以超过 Go 预先定义的 类型所支持的精度.
-
Constant expression 的运算规则; 如下分类:
-
一元运算符; apply to untyped constants results in an untyped constant of the same kind. 大概就是: 当 operand 是 typed constant 时, 结果也是 typed constant, 并且类型与 operand 一致. 当 operand 是 untyped constant 时, 结果也是 untyped constant, 而且与 operand 具有一样的 kind.
The mask used by the unary bitwise complement operator
^
取值参见原文. -
二元运算符;
-
shift expression; If the left operand is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand.
-
comparison expression; A constant comparison always yields an untyped boolean constant.
-
其他; Except for shift operations, if the operands of a binary operation are different kinds of untyped constants, the operation and, for non-boolean operations, the result use the kind that appears later in this list: integer, rune, floating-point, complex.
-
-
Order of evaluation
-
Explicit parentheses affect the evaluation by overriding the default associativity. In the expression
x + (y + z)
the additiony + z
is performed before addingx
. -
At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations. Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression. 按照我的理解就是, 首先由 initialization dependencies 确定整体 initialization expressions 的求值顺序, 在求值顺序确定之后, initialization expressions 内部的求值仍然 遵循从左到右.
注意, 这里仍然有求值顺序 not specified 的情况存在. 具体参考原文.
Statements
- Statements control execution. 这里 execution 我觉得是执行流程的语义, 所以这句话是指语句控制着流程.
Terminating statements
- Terminating statements, 原文指定了一揽子规则确定哪些 statement 是 terminating 的.
- not terminating. 除了 terminating statement 之外的语句都是 not terminating.
- A statement list ends in a terminating statement if the list is not empty and its final non-empty statement is terminating.
Empty statements
- empty statement does nothing.
Labeled statements
- 按照我的理解, labeled statements 就是一个 statement 只不过被 labeled 的了, 而且 labeled statement
可以作为
goto
,break
,continue
的目标.
Expression statements
-
With the exception of specific built-in functions, function and method calls and receive operations can appear in statement context. Such statements may be parenthesized.
某些 built-in functions are not permitted in statement context. 如下:
func main() { d := float64(-1.5) i := int(d) s := "Hello"; len(s); // illegal if len is the built-in function fmt.Println(d, i) }
Send statements
- 语义: A send statement sends a value on a channel.
-
语法: 参见原文. 不过有以下可以注意:
- Both the channel and the value expression are evaluated before communication begins.
- A send on a
nil
channel blocks forever. 为啥这里不 panic 呢?
IncDec statements
-
The
++
and--
statements increment or decrement their operands by the untyped constant1
.只要注意,
++
,--
这里是 statement, 而不是 operater.
Assignments
-
参见原文了解其语法形式.
In assignments, each value must be assignable to the type of the operand to which it is assigned, with the following special cases, 参见原文.
-
assignment operation
x op= y
, 等同于x = x op (y)
, 只不过这里x
仅被求值一次.x
,y
作为 expression list, must contain exactly one single-valued expression. -
tuple assignment, 左侧: a list of variables; 右侧: a multi-valued operation, 可以是 single multi-valued expression, 或者是 multiple single-valued expression.
-
assignment proceeds in two phases. 具体参见原文. 按照我的理解, 这两个阶段是:
- 首先对左侧 expression 进行求值, 保存每一个 expression 的地址; 然后对右侧 expression 进行 求值, 保存每一个 expression 的值.
- 按照顺序, 将右侧的值赋值给左侧的地址指向的变量.
If statements
-
参见原文了解其语法形式, 值得注意的是:
-
expression 必须是 boolean expression.
-
simple statement executes before the expression is evaluated.
-
Switch statements
-
There are two forms: expression switches and type switches. In an expression switch, the cases contain expressions that are compared against the value of the switch expression. In a type switch, the cases contain types that are compared against the type of a specially annotated switch expression.
-
The switch expression is evaluated exactly once in a switch statement.
-
若 switch expression 之前有 simple statement, 则 simple statement executes before the expression is evaluated.
Expression switches
Expression switcher 很类似 C++ 中的 switch 语句, 这里仅介绍一些比较个性的特点:
-
A missing switch expression is equivalent to the boolean value
true
. -
case expressions, which need not be constants, are evaluated left-to-right and top-to-bottom. 并且 case expression 仅在需要求值的时候才会被求值. 如下:
func F(a int) int { return a } func main() { switch 1 { case F(0): fmt.Println(0); case F(1): fmt.Println(1); case F(2): // 不会被求值. 因为不会执行到. fmt.Println(2); } }
func main() { switch 1 { case F(1): fmt.Println(1); fallthrough case F(0): // 不会被求值, 因为无论求值与否, 其对应的 clause 总会被执行. fmt.Println(0); case F(2): // 不会被求值. 因为不会执行到. fmt.Println(2); } }
case expressions 在 switch expression 之后执行, 因此 switch expresion 中的副作用对 case expression 可见. 这个规则是我加的, 未在原文中找到明显条例. 其实也没啥, 因为 switch expression 作为一个 expresion 而言, 是没啥副作用的, 有副作用的都被 Golang 当作 statement 来处理了.
-
There can be at most one default case and it may appear anywhere in the “switch” statement. 注意: 任何位置. 不过我好像一直把
default
放在最后. -
当 switch expresion 是 untyped, 以及当 case expression 是 untyped 的处理措施.
the switch expression is treated as if it were used to declare and initialize a temporary variable t without explicit type, 即 switch expresion 可以看作:
t := switch expresion
. -
fallthrough, 参见 fallthrough 语句.
Type switches
- switch expreesion 的语法格式,
x.(type)
. -
switch statement 的执行规则, 具体参考原文, 这里是总结:
- 若
x
是nil
, 则匹配case nil
. - 否则, 使用
x
的 dynamic type 来进行匹配.
- 若
- 当 switch expreesion 是个 short variable declaration. 第一注意此时变量的作用域是 each clause 对 应的 block, 而不是整个 switch statement 对应的 block, 第二注意变量的类型是啥.
For statements
A “for” statement specifies repeated execution of a block. 有三种形式.
-
Condition for; 具体语法规则参考原文, 只需要注意若 condition expression 省略, 默认为
true
. -
ForClause; 与 C++ 完全一致, 具体细节可以参考原文.
-
RangeClause; iterates through all entries of an array, slice, string or map, or values received on a channel.
range expression; 如下几种类型的 range expression 需要注意:
-
string; 参见原文了解当 range expression 是 string 时, iteration values 的生成规则, 并不是按照字节来遍历.
-
map; 当 range expresion 为
nil
时, 遍历次数为 0. 注意了解当在遍历过程中移除, 或者 增加元素时, 行为如何. -
channel;
nil
channel 会永久阻塞. 此时会一直循环直至 channel 被 closed.
range variable;
-
当是 short variable declaration 这种 form 时, 注意了解此时变量的作用域, 本来我想的是, 此时变量的作用域从迭代开始到迭代结束, 每一次迭代都会创建新的变量; 但是原文讲的和我想的正好相反.
-
If the last iteration variable is the blank identifier, the range clause is equivalent to the same clause without that identifier.
RangeClause 的执行:
-
对 range expression 进行求值; The range expression is evaluated once before beginning the loop. 注意原文指定这里有个例外, 关于这个例外, 我也不是很懂, 参考这里.
-
确定 iteration values 的取值. 这里可以参考原文.
-
The iteration values are assigned to the respective iteration variables as in an assignment statement. 按照 assignment statement 的规则将 iteration values 赋值给 iteration variables.
-
Go statements
-
goroutine; independent concurrent thread of control; within the same address space. 其实可以把 goroutine 作为线程来进行理解. 很多行为很是相似.
-
A “go” statement starts the execution of a function call as an goroutine. 也就是首先 function value and parameters are evaluated as usual in the calling goroutine; 然后 创建一个新的 goroutine, 并在新的 goroutine 中执行函数. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.
Select statements
-
Select 的语义, 从一组 send, recv operations 中选择一个来进行处理.
Select 语法; 参见原文, 在 select 中有且仅有一个 default clause, 并且可以出现在任何位置.
-
Select 的执行顺序, 参见原文.
Return statements
-
if the function’s result type specifies names for its result parameters, The result parameters act as ordinary local variables, all the result values are initialized to the zero values for their type upon entry to the function.
-
return 的语义, 这个已经不用多说了; 接下来主要讲述 return 的语法, 具体参考原文, 这里只是 介绍一下结构:
-
a function without a result type, 其 return 是什么样子的.
-
a function with a result type, 其 return 有三种形式, 具体哪三种参考原文.
-
Break statements
-
A “break” statement terminates execution of “for”, “switch”, or “select” statement. 按照我的理解, terminates execution 意味着 control flow to “for”, “switch”, or “select” statement 的下一句语句.
若 “break” 后未跟 label, 则表明 terminates innermost “for”, “switch”, or “select” statement within the same function.
If there is a label, it must be that of an enclosing “for”, “switch”, or “select” statement, and that is the one whose execution terminates. 即此时 terminates 指定的 “for”, “switch”, or “select” statement,
Continue statements
- Continue 语句用来终止当前迭代, 开始下一次迭代. label 指定了 continue 作用于哪个循环. 若未加 label, 则表明是 innermost for loop.
Goto statements
- A “goto” statement transfers control to the statement with the corresponding label. 其实就这 一句话就行了, 原文还介绍了一些 goto 的局限之处, 这些我觉得没必要了解, 编译器会在你使用 goto 姿势不对的时候拒绝编译.
Fallthrough statements
- A “fallthrough” statement transfers control to the first statement of the next clause in an expression “switch” statement. 原文还介绍了一些 fallthrough 的局限之处.
Defer statements
-
defer statement 与 go statement 很像很像; 按照我的理解, defer 用来注册函数, 其注册的函数会在以下 条件时才会执行:
-
the surrounding function returns; either because the surrounding function executed a return statement, or reached the end of its function body.
-
the corresponding goroutine is panicking.
-
-
defer 的执行流程:
- 对 defer 后面跟的 function value, 参数进行求值; 并且保存他们的值(不是地址).
-
将 defer 对应的 function value 以及参数追加到某种栈结构中, 这样意味着在执行 defer 注册的函数时, 会按照先进后出的顺序来.
在压栈的时候不会检查参数的合法性, 因此 If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.
-
一个栗子; 可以 if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned. I
Built-in functions
- The built-in functions do not have standard Go types, 也即 built-in functions 并不能 作为 function values 赋值给某种 function type 类型的变量了.
Close
func close(arg channel);
- 关闭
arg
, 此后 no more values will be sent on the channel. 此时对于接收方的行为可以参考: receive operation 节.
Length and capacity
-
len()
,cap()
的函数原型, 以及语义. 原型如下, 至于语义参考原文:func len(s) int; // 关于 s 的可取类型, 参考原文. func cap(s) int; // 关于 s 的可取类型, 参考原文.
-
len(s)
,cap(s)
某些情况下s
不会被求值, 并且len(s)
,cap(s)
结果是个常数. 具体 哪些情况参考原文.
Allocation
new(T) *T;
- The built-in function
new
takes a typeT
, allocates storage for a variable of that type at run time, The variable is initialized as described in the section on initial values.
Making slices, maps and channels
-
make()
的原型以及语义参考原文, 这里列举了一下值得注意的点:-
It returns a value of type
T
(not*T
). 按照我的理解是, slice, map, channel 这些类型 本身就具有引用的语义, 即这些类型本身就像是指针类型. -
The size arguments
n
andm
must be of integer type or untyped. A constant size argument must be representable by a value of typeint
.
-
Appending to and copying slices
append()
,copy()
the result is independent of whether the memory referenced by the arguments overlaps. 也即append()
,copy()
类似与memmove()
, 已经考虑到了内存重叠 的可能性.
append(s S, x ...T) S;
-
此时
S
是 slice type,T
is the element type ofS
, 这里是往s
中追加元素.当
S
为[]byte
时, 有种特殊形式, 即 second argument of string type followed by...
, 具体参考 原文.If the capacity of
s
is not large enough to fit the additional values,append
allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise,append
re-uses the underlying array.
copy(dst, src []T) int
copy(dst []byte, src string) int
- 拷贝元素, 这里只需要注意: The number of elements copied is the minimum of
len(src)
andlen(dst)
. RETURN
: The number of elements copied.
Deletion of map elements
- 参见原文. 只需要注意: If the map
m
isnil
or the elementm[k]
does not exist,delete
is a no-op.
Manipulating complex numbers
复数, 未看.
Handling panics
首先原文内容结构如下:
- panicking 概念, 以及 termination sequence 流程.
recover()
语义, 原文是用一个栗子来表明的; 以及recover()
何时会返回nil
.
然后我的理解如下:
Q1: 当 termination sequence 的过程中发生了 panic 怎么办?
A1: 这里按照我的理解, 当某个 goroutine 发生了 panic(包括显式 panic()
调用以及 run-time panic) 时,
会在当前 goroutine 上设置一个标志, 然后 termination sequence, 当 termination sequence 的过程中再一次
panic 时, 会继续按照 termination sequence 来进行, 即中止当前函数的执行, 然后依次执行 defer, 相当于覆盖
上一次 panic.
Q2: 多次调用 recover()
会有什么效果?
A2: 这里按照我的理解, 首先定义一个概念, 无效的 recover()
调用, 即哪些 recover()
was not called
directly by a deferred function; 有效的 recover()
调用, 除无效的 recover()
调用之外的都是有效的.
对于无效的 recover()
调用, 可以视为是 no-op.
然后有效的 recover()
调用, 其逻辑如下:
if (当前 goroutine panicking)
清除 panicking 标志;
return panicking 结果;
else
no-op;
Bootstrapping
未看
Packages
- 介绍了 Go Programs, packages, source files 三者之间的关系
Source file organization
- 参见原文, 指定了 source file 的结构. 注意概念: package clause, import declarations.
Package clause
- 语义: A package clause defines the package to which the file belongs.
-
语法: 参见原文;
An implementation may require that all source files for a package inhabit the same directory.
Import declarations
-
语义: import declarations that declare packages whose contents it(是指当前 source file) wishes to use.
An import declaration declares a dependency relation between the importing and imported package.
-
语法: 参见原文, 注意以下几点:
-
PackageName; The PackageName is used in qualified identifiers to access exported identifiers of the package within the importing source file.
PackageName 的作用域.
当 PackageName 被省略时;
当 PackageName 是一个
.
时;当 PackageName 是 blank identifie 时;
-
ImportPath; The interpretation of the ImportPath is implementation-dependent, 一般是 compiled package 的相对路径, 至于相对于啥, 则是 implementation-dependent.
-
An example package
略
Program initialization and execution
The zero value
- 对于变量而言, 若未显式指定其初值, 则取 zero value for its type.
Program execution
- complete program, main package 的概念.
-
Program execution begins by initializing the main package and then invoking the function
main
. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.关于 initializing the main package 的流程, 参见下文 Package initialization.
Package initialization
-
Package initialization (variable initialization and the invocation of
init
functions) happens in a single goroutine; 我觉得就是 main goroutine, 但是原文没提.initializing the main package 的流程如下:
- 根据 main package 以及其依赖的 package 之间的依赖关系, 建立一个依赖树. 对这个树按层次遍历, 对于遍历中每一个 package, 执行 package initialization.
package initialization 的流程如下:
- 执行 package-level variables initialization. 具体细节参见下文.
- 执行
init
function. 具体细节参见下文.
-
package-level variables initialization; 首先介绍若干概念:
-
declaration order; 同一 source file 内 package-level variables 的 declaration order 很显然; The declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler(其实我觉得应该是 source file 提交给链接器的顺序, 可以参考 C++ 全局变量的初始化).
-
ready for initialization; 参见原文了解这个概念.
-
Dependency analysis;
-
概念; does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively.
Dependency analysis is performed per package; only references referring to variables, functions, and methods declared in the current package are considered. package 与 package 之 间的 dependency 由
import
确定. -
三条规则, 参见原文进行了解.
-
Initialization proceeds(也就是 package-level variables initialization 的流程); repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.
If any variables are still uninitialized when this process ends, those variables are part of one or more initialization cycles, and the program is not valid. 此时循环依赖了.
-
-
init
function 的执行; 首先介绍init
function 的概念:init
function; Theinit
identifier is not declared and thusinit
functions cannot be referred to from anywhere in a program. 可以理解 package level 中的名为init
的 function 是匿名函数,init
表明这个函数需要初始化阶段执行.
calling all
init
functions in the order they appear in the source, possibly in multiple files, as presented to the compiler.
Errors
-
predeclared type
error
的定义:type error interface { Error() string }
-
一种约定; 具体参见原文.
Run-time panics
-
a run-time panic equivalent to a call of the built-in function
panic
with a value of the implementation-defined interface typeruntime.Error
. 其中runtime.Error
的定义如下:package runtime type Error interface { error // and perhaps other methods }
System considerations
Package unsafe
-
unsafe
built-in package, provides facilities for low-level programming, may not be portable, must be vetted manually for type safety.接下来开始介绍
unsafe
提供的 facilities. -
Pointer
type Pointer *T
-
A
Pointer
is a pointer type but aPointer
value may not be dereferenced; 类似与 C++ 中的void*
.Any pointer or value of underlying type
uintptr
can be converted to aPointer
type and vice versa. The effect of converting betweenPointer
anduintptr
is implementation-defined.
- Sizeof
func Sizeof(x T) uintptr
RETURN
: return size of a hypothetical variablev
as ifv
was declared viavar v = x
.- Calls to
Alignof
,Offsetof
, andSizeof
are compile-time constant expressions.
- Offsetof
func Offsetof(selector T) uintptr
-
这里
selector
形式为s.f
, denoting a fieldf
of the struct denoted bys
or*s
, and returns the field offset in bytes relative to the struct’s address.If
f
is an embedded field, it must be reachable without pointer indirections through fields of the struct.
- Alignof
func Alignof(variable ArbitraryType) uintptr
这里与 C++11 中的 alignof
很是相似.
-
首先介绍 alignment 概念, golang 中每一个 type 都有 alignment 属性, 该类型的变量的地址总是其 type’s alignment 的整数倍. 之所以具有这种要求, 是因为 Computer architectures may require memory addresses to be aligned.
The function
Alignof
takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes.
Size and alignment guarantees
- 介绍了若干个预定义类型(如
int
,float32
)的 size, 具体参考原文. - Golang 对 alignment properties 的三条保证, 具体参考原文.
-
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero.
Two distinct zero-size variables may have the same address in memory. 如:
type S struct { } type A struct { s1 S s2 S } func main() { fmt.Println(unsafe.Sizeof(S{})); // 0 fmt.Println(unsafe.Sizeof(A{})); // 0 a := A{} fmt.Println(unsafe.Offsetof(a.s1)); // 0 fmt.Println(unsafe.Offsetof(a.s2)); // 0 }