概述
Go语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口(interface)的概念来实现多态性。很多重要的开源项目都是使用Go语言开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。
Go 是编译型语言
Go 使用编译器来编译代码。编译器将源代码编译成二进制(或字节码)格式;在编译代码时,编译器检查错误、优化性能并输出可在不同平台上运行的二进制文件。要创建并运行 Go 程序,程序员必须执行如下步骤。
使用文本编辑器创建 Go 程序;
保存文件;
编译程序;
运行编译得到的可执行文件。
这不同于 Python、Ruby 和 JavaScript 等语言,它们不包含编译步骤。Go 自带了编译器,因此无须单独安装编译器。
内存分配
内存分配器完整保留了 tcmalloc 的原始架构。使用 cache 为当前执行线程提供无锁分配,多个 central 在不同线程间平衡内存单元复用。在更高层次里,heap 则管理着大块内存,用以切分成不同等级的复用内存块。快速分配和二级内存平衡机制,让内存分配器能优秀地完成高压力下的内存管理任务。
变量声明
var name type
go的基本数据类型如下:
- bool
- string
- int、int8、int16、int32、int64
- uint、uint8、uint16、uint32、uint64、uintptr
- byte // uint8 的别名 rune //
- int32 的别名 代表一个 Unicode 码 float32、float64 complex64、complex128
变量被声明之后,系统自动赋予它该类型的零值:
- 整型和浮点型变量的默认值为 0 和 0.0。
- 字符串变量的默认值为空字符串。
- 布尔型变量默认为 false。
- 切片、函数、指针变量的默认为nil。
除 var 关键字外,还可使用更加简短的变量定义和初始化语法。 名字 := 表达式,简短模式有以下限制:
- 定义变量,同时显式初始化。
- 不能提供数据类型。
- 只能用在函数内部。
注意:由于使用了:=,而不是赋值的=,因此推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。
// 声明 hp 变量
var hp int
// 再次声明并赋值
hp := 10
编译会报错
i, j := 0, 1 也支持这种同时多个变量赋值
变量初始化的标准格式:
var 变量名 类型 = 表达式 var hp int = 100
var hp = 100 将 int 省略后,编译器会尝试根据等号右边的表达式推导 hp 变量的类型。
匿名变量
匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:
func GetData() (int, int) {
return 100, 200
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
//运行结果100 200 匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
变量的作用域
- 局部变量
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量
形式参数会作为函数的局部变量来使用
func main() {
//声明局部变量 a 和 b 并赋值
var a int = 3
var b int = 4
//声明局部变量 c 并计算 a 和 b 的和
c := a + b
fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
//a = 3, b = 4, c = 7
- 全局变量
在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用
//声明全局变量
var c int
func main() {
//声明局部变量
var a, b int
//初始化参数
a = 3
b = 4
c = a + b
fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
//a = 3, b = 4, c = 7
//Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。
一些类型说明
-
程序逻辑对整型范围没有特殊需求。例如,对象的长度使用内建 len() 函数返回,这个长度可以根据不同平台的字节长度进行变化。实际使用中,切片或 map 的元素数量等都可以用 int 来表示。反之,在二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用 int 和 uint。
-
Go语言中不允许将整型强制转换为布尔型,代码如下
var n bool
fmt.Println(int(n) * 2) 报错 -
Go语言的字符有以下两种:
一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。
另一种是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。 -
Go没有隐式类型转换,所有转换必须显示进行。类型 B 的值 = 类型 B(类型 A 的值) a := 5.0 a := 5.0;类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将 int16 转换为 int32)。当从一个取值范围较大的类型转换到取值范围较小的类型时(将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。只有相同底层类型的变量之间可以进行相互转换(如将 int16 类型转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)
指针
默认值是nil,每个变量在内存中都有它的地址,指针通过取地址操作符获得变量或者对象的地址,ptr := &v // v 的类型为 T。ptr 的类型为T。ptr := new(string),也可以定义指针变量。通过解引用操作符可以操作指针变量,同时修改指向对象的值。*ptr = xxx;指针和c/c++很像。
go run -gcflags “-m -l” -gcflags 参数是编译参数。其中 -m 表示进行内存分配分析,-l 表示避免程序内联,也就是避免进行程序优化,通过这个可以查看程序运行,变量内存分配情况
变量逃逸:c/c++程序需要关心各种内存分配,是在栈上还是在堆上,go致力于把程序员从这种痛苦中解救出来。变量是否被取地址,是否发生逃逸,编译器自动选择如何处理。