-
go文件结构:
package
,创建包。Go语言以“包”作为管理单位,每个 Go 源文件必须先声明它所属的包。包名可以与其目录名不同。main 包
是Go语言程序的入口包,一个Go语言程序必须有且仅有一个 main 包。如果一个程序没有 main 包,那么编译时将会出错,无法生成可执行文件。import "name"
导入包,多个包用{ }括起来。
-
语句结尾不需要
;
,Go 编译器会自动添加,自己加也可以。
1 变量
变量的基本类型
-
bool
:true/false布尔型无法参与数值运算,也无法与其他类型进行转换。
-
string
UTF-8编码的变宽字符序列,每个字符都用一个或多个字节表示(Java是采用UTF-16来表示,每个字符都对应2个字节)。一般的比较运算符(==、!=、<、<=、>=、>)是通过在内存中按字节比较来实现字符串比较的,比较的是字符串自然编码的顺序。
字符串所占的字节长度可以通过函数 len() 来获取,例如 len(str)。 -
有符号整型
int
(依赖不同平台的实现 32或64位)、int8
(-128 到 127)、int16
(-32768 到 32767)、int32
(又名rune
-2147483648 到 2147483647)、int64
-
无符号整型
uint
(与int一样)、uint8
(又名byte
0~255)、uint16
(0 到 65535)、uint32
(0 到 4294967295)、uint64
、uintptr
(存放指针,位数依赖平台)在二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用 int 和 uint。
-
float32
、float64
:float32能取到的最大值为math.MaxFloat32
,精度大约十个十进制数;float64精度约15个十进制数。 -
complex64
、complex128
:复数,实部虚部都是float32,complex128 为复数的默认类型。声明复数:var name complex128 = complex(x, y)
real(z) 来获得复数z的实部;通过imag(z) 获得复数z的虚部
变量的声明
-
声明变量:
var name type
变量类型放在变量名之后。 -
当一个变量被声明之后,系统自动赋予它该类型的零值。所有的内存在 Go 中都是经过初始化的。
在C语言中,变量在声明时,并不会对变量对应内存区域进行清理操作。此时,变量值可能是完全不可预期的结果。所以声明时要初始化。微软的 VC 编译器会将未初始化的栈空间以 16 进制的 0xCC 填充,而未初始化的堆空间使用 0xCD 填充,而 0xCCCC 和 0xCDCD 在中文的 GB2312 编码中刚好对应“烫”和“屯”字。
-
变量初始化:
var 变量名 类型 = 表达式
例如:var a int = 100
编译器会根据等号右边的表达式推导变量的类型,因此可以省略类型。
编译器会尽量提高精确度,以避免计算中的精度损失,浮点型会推导为float64 -
批量声明:
var ( a int b string c []float32 d func() bool //d是返回值是bool的函数名 )
-
简短声明格式:
变量 := 表达式
例如:i,j := 0,1
该声明方式只能用在函数内部,定义变量的同时显式初始化,不能提供数据类型。
如果已经声明过变量,再使用短变量声明并初始化则会编译错误。也就是说短变量声明的左值必须是没有定义过的变量。注意:在多个短变量声明和赋值中,至少有一个新声明的变量出现在左值中,即便其他变量名可能是重复声明的,编译器也不会报错。例如:
//net.Dial按指定协议和地址发起网络连接,函数有两个返回值,一个是连接对象(conn),一个是错误对象(err) conn, err := net.Dial("tcp", "127.0.0.1:8080") conn2, err := net.Dial("tcp", "127.0.0.1:8080") //conn2是新定义的变量,即使err已经声明过编译也不会报重复定义
变量的多重赋值
- 由于Go中多个变量可以同时赋值:
i, j = 0, 1
,所以可能简单的进行变量交换b, a = a, b
匿名变量
'_'
用下划线(空白标识符)声明的变量,任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。- 在使用多重赋值时,如果不需要在左值中接收变量,可以使用匿名变量。(或者只想接收函数部分返回值时)
- 匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
变量作用域
- 在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。
- 局部变量不是一直存在的,它只在定义它的函数被调用后存在,函数调用结束后这个局部变量就会被销毁。
- 在函数体外声明的变量称之为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用(不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量)。
- 全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。
- Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。
- 在定义函数时函数名后面括号中的变量叫做形式参数。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。
形式参数会作为函数的局部变量来使用。
字符串
-
Go语言中字符串的内部实现使用 UTF-8 编码。
-
字符串拼接:
str := "aaa" + "bbb"
-
多行字符串:多行字符串用反引号包起来,可以跨行,其中的转义字符无效,会被原样输出。
-
字符串长度:
len()
返回int型,表示字符串的 ASCII 字符个数或字节长度。例如:len("中国") = 6
如果希望按习惯上的字符个数来计算,需要使用 Go 语言中 UTF-8 包提供的 RuneCountInString() 函数:
utf8.RuneCountInString("中国") = 2
ASCII 字符串长度使用 len() 函数。
Unicode 字符串长度使用 utf8.RuneCountInString() 函数。 -
字符串遍历:ASCII 字符串遍历直接使用下标。Unicode 字符串遍历用 for range。
theme := "狙击 start" for i := 0; i < len(theme); i++ { fmt.Printf("ascii: %c %d\n", theme[i], theme[i]) } for _, s := range theme { fmt.Printf("Unicode: %c %d\n", s, s) }
-
修改字符串:Go 语言的字符串是不可变的,修改字符串时,可以将字符串转换为 []byte 进行修改,[]byte 和 string 可以通过强制类型转换互转。
angel := "Heros never die" angleBytes := []byte(angel) for i := 5; i <= 10; i++ { angleBytes[i] = ' ' } fmt.Println(string(angleBytes)) //输出:Heros die
字符串不可变有很多好处,如天生线程安全,大家使用的都是只读对象,无须加锁;再者,方便内存共享,而不必使用写时复制(Copy On Write)等技术;字符串 hash 值也只需要制作一份。
代码中实际修改的是 []byte,[]byte 在 Go 语言中是可变的,本身就是一个切片。 -
截取字符串:
str[起始位置:结束为止]
, 结束位置省略表示到末尾。 -
字符类型:uint8(byte)代表一个ASCII码字符,rune(int32) 代表一个UTF-8字符。
Unicode 包中内置了一些用于测试字符的函数:判断是否为字母:unicode.IsLetter(ch)
判断是否为数字:unicode.IsDigit(ch)
判断是否为空白符号:unicode.IsSpace(ch)
数据类型转换
- Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明。
- 只有相同底层类型的变量之间可以进行相互转换(如将 int16 类型转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)。
变量逃逸分析
-
在 C/C++ 语言中,需要开发者自己学习如何进行内存分配,选用怎样的内存分配方式来适应不同的算法需求。比如,函数局部变量尽量使用栈,全局变量、结构体成员使用堆分配等。Go语言将这个过程整合到了编译器中,命名为“变量逃逸分析”。通过编译器分析代码的特征和代码的生命周期,决定应该使用堆还是栈来进行内存分配。
-
逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。也是就是说逃逸分析是解决指针作用范围的编译优化方法。函数中生成一个新对象:
如果分配到栈上,待函数返回资源就被回收了
如果分配到堆上,函数返回后交给gc来管理该对象资源栈资源的分配及回收速度比堆要快,所以逃逸分析最大的好处应该是减少了GC的压力。
-
运行代码时使用
go run -gcflags "-m -l" main.go
,可以进行变量逃逸分析。-gcflags 参数是编译参数
-m 表示进行内存分配分析
-l 表示避免程序内联,也就是避免进行程序优化。 -
变量逃逸情况分很多种,例如栈空间不足逃逸
2 指针
- Go语言为程序员提供了控制数据结构指针的能力,但是,并不能进行指针运算。
- 指针(pointer)在Go语言中可以被拆分为两个核心概念:
类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
切片,由指向起始元素的原始指针、元素数量和容量组成。 - 对变量进行取地址操作使用
&
操作符,可以获得这个变量的指针变量。
指针变量的值是指针地址。
对指针变量进行取值操作使用*
操作符,可以获得指针变量指向的原变量的值。
【参考文档】
Go语言基本语法