共计 3618 个字符,预计需要花费 10 分钟才能阅读完成。
—Tony Bai · Go 语言第一课
在编程语言中,为了不便操作内存特定地位的数据,咱们用一个特定的名字与位于特定位
置的内存块绑定在一起,这个名字被称为变量。变量所绑定的内存区域是要有一个明确的边界的。
Go 是动态语言,所有变量在应用前必须先进行申明。申明的意义在于通知编译器该变量能够操作的内存的边界信息,而这种边界通常又是由变量的类型信息提供的。
变量分类:
Go 语言的变量能够分为两类:
- 一类称为包级变量 (package varible),也就是在包级别可见的变量。如果是导出变量(大写字母结尾),那么这个包级变量也能够被视为全局变量;(包级变量只能应用带有 var 关键字的变量申明模式,不能应用短变量申明模式.)
- 另一类则是局部变量 (local varible),也就是 Go 函数或办法体内申明的变量,仅在函数或办法体内可见。
代码块与作用域
Go 语言中的代码块是 包裹在一对大括号外部的申明和语句序列,如果一对大括号外部没有任何申明或其余语句,咱们就把它叫做空代码块。
一个标识符的作用域就是指这个标识符在被申明后能够被无效应用的源码区域.
依照 Go 语言定义,一个标识符要成为导出标识符需同时具备两个条件:一是这个标识符申明在包代码块中,或者它是一个字段名或办法名;二是它名字第一个字符是一个大写的 Unicode 字符。
变量遮蔽
变量遮蔽问题的根本原因,就是内层代码块中申明了一个与外层代码块同名且同类型的变量,这样,内层代码块中的同名变量就会代替那个外层变量,参加此层代码块内的相干计算,咱们也就说内层变量遮蔽了外层同名变量。
根本数据类型
Go 语言的类型大体可分为 根本数据类型、复合数据类型和接口类型 这三种.
平台相干整型,它们的长度会依据运行平台的扭转而扭转。Go 语言原生提供了三个平台相干整型,它们是 int、uint 与 uintptr
因为这三个类型的长度是平台相干的,所以咱们在编写有移植性要求的代码时,千万不要强依赖这些类型的长度.
整型的溢出问题
如果某个整型因为参加某个运算,导致后果超出了这个整型的值边界,咱们就说产生了整型溢出的问题。学习整型时你要特地留神,每个整型都有本人的取值范畴和示意边界,一旦超出边界,便会呈现溢出问题。
一个浮点数被分为符号位、阶码与尾数三个局部。
浮点数在内存中的二进制示意分三个局部:符号位、阶码(即通过换算的指数),以及尾数。这样示意的一个浮点数,它的值等于:
咱们首先来看单精度(float32)与双精度(float64)浮点数在阶码和尾数上的不同
咱们来看看如何将一个十进制模式的浮点值 139.8125,转换为 IEEE 754 规
定中的那种单精度二进制示意。步骤一:咱们要把这个浮点数值的整数局部和小数 局部,别离转换为二进制模式(后缀 d
示意十进制数,后缀 b 示意二进制数):这样,原浮点值 139.8125d 进行二进制转换后,就变成 10001011.1101b
步骤二:挪动小数点,直到整数局部仅有一个 1,也就是 10001011.1101b =>
1.00010111101b。咱们看到,为了整数局部仅保留一个 1,小数点向左移了 7 位,这样
指数就为 7,尾数为 00010111101b
步骤三:计算阶码
IEEE754 规定不能将小数点挪动而失去的指数,始终填到阶码局部,指数到阶码还须要一
个转换过程。对于 float32 的单精度浮点数而言,阶码 = 指数 + 偏移值。偏移值的计算公
式为 2^(e-1)-1,其中 e 为阶码局部的 bit 位数,这里为 8,于是单精度浮点数的阶码偏移
值就为 2^(8-1)-1 = 127。这样在这个例子中,阶码 = 7 + 127 = 134d = 10000110b
float64 的双精度浮点数的阶码计算也是这样的。步骤四:将符号位、阶码和尾数填到各自地位,失去最终浮点数的二进制示意。尾数位数
有余 23 位,可在前面补 0
这样,最终浮点数 139.8125d 的二进制示意就为
0b_0_10000110_00010111101_000000000000
但日常应用中咱们尽量应用 float64,这样不容易呈现浮点溢出的问题。
math.MaxFloat32 示意 float32 能示意的最大数值,大概是 3.4e38;对应的 math.MaxFloat64 常量大概是 1.8e308
Go 提供的类型定义语法,来创立自定义的数值类型,咱们能够通过 type 关键字基于原生数值类型来申明一个新类型。type MyInt int32
依据 Go 的类型平安规定,咱们无奈间接让它们互相赋值,或者是把它们放在同一个运算中间接计算,这样编译器就会报错。咱们须要借助显式转型再进行运算
咱们也能够通过 Go 提供的类型别名(Type Alias)语法来自定义数值类型。和下面应用
规范 type 语法的定义不同的是,通过类型别名语法定义的新类型与原类型别无二致,能够
齐全互相代替。
type MyInt = int32
var n int32 = 6
var a MyInt = n // ok
通过类型别名定义的 MyInt 与 int32 齐全等价,所以这个时候两种类型就是同一种类型,不再须要显式转型,就能够互相赋值。
字符串类型
Go 语言规定,字符串类型的值在它的生命周期内是不可扭转的。
string 类型其实是一个“描述符”,它自身并不真正存储字符串数据,而仅是由一个指向底层存储的指针和字符串的长度字段组成的。
Go 提供的内置函数 len,咱们只能获取字符串内容的长度(字节个数)。获取字符串中字符个数更业余的办法,是调用规范库 UTF-8 包中的 RuneCountInString 函数。
Go 原生反对通过 +/+= 操作符进行字符串连贯, 除此之外 Go 还提供了 strings.Builder、strings.Join、fmt.Sprintf 等函数来进行字符串连贯操作.
** 惯例 for 迭代与 for range 迭代字符串所失去的后果不同,惯例 for 迭代采纳的是字节视
角;而 for range 迭代采纳的是字符视角。**
常量:
Go 常量一旦申明并被初始化后,它的值在整个程序的生命周期内便放弃不变。Go 语言引入 const 关键字来申明常量.
iota 是 Go 语言的一个预约义标识符,它示意的是 const 申明块(包含单行申明)中,每个常量所处地位在块中的偏移值(从零开始)。
从定长数组到变长切片
切片类型
type slice struct {
array unsafe.Pointer
len int
cap int
}
array: 是指向底层数组的指针;
len: 是切片的长度,即切片中以后元素的个数;
cap: 是底层数组的长度,也是切片的最大容量,cap 值永远大于等于 len 值。
如何创立切片:
办法一:通过 make 函数来创立切片,并指定底层数组的长度。
办法二:采纳 array[low : high : max] 语法基于一个已存在的数组创立切片。这种形式被称为数组的切片化.
切片与数组最大的不同,就在于其长度的不定长,这种不定长须要 Go 运行时提供反对,这种反对就是切片的“动静扩容”。数组的长度是确定的,而切片,咱们能够了解为一种“动静数组”,它的长度在运行时是可变的 .
Map 类型
map 类型对 value 的类型没有限度,然而对 key 的类型却有严格要求,因为 map 类型要保障 key 的唯一性。Go 语言中要求,key 的类型必须反对“==”和“!=”两种比拟操作符。
map 类型,因为它外部实现的复杂性,无奈“零值可用”。所以,如果咱们对处于零值状态的 map 变量间接进行操作,就会导致运行时异样(panic),从而导致程序过程异样退出.
应用 make 为 map 类型变量进行显式初始化,map 类型的容量不会受限于它的初始容量值,当其中的键值对数量超过初始容量后,Go 运行时会主动减少 map 类型的容量,保障后续键值对的失常插入.
Go 语言的 map 类型反对通过用一种名为“comma ok”的习用法,进行对某个 key 的查问。
m := make(map[string]int)
v, ok := m["key1"]
if !ok {// "key1" 不在 map 中} /
/ "key1" 在 map 中,v 将被赋予 "key1" 键对应的 value
在 Go 语言中,请应用“comma ok”习用法对 map 进行键查找和键值读取操作.
在 Go 中,咱们须要借助内置函数 delete 来从 map 中删除数据。应用 delete 函数的状况下,传入的第一个参数是咱们的 map 类型变量,第二个参数就是咱们想要删除的键.
m := map[string]int {
"key1" : 1,
"key2" : 2,
} f
mt.Println(m) // map[key1:1 key2:2]
delete(m, "key2") // 删除 "key2"
fmt.Println(m) // map[key1:1]
对同一 map 做屡次遍历的时候,每次遍历元素的秩序都不雷同
- 不要依赖 map 的元素遍历程序;
- map 不是线程平安的,不反对并发读写;
- 不要尝试获取 map 中元素(value)的地址。
map 深度解析: https://www.qcrao.com/2019/05…