Study Golang — (2) 基本数据

Go的数据类型分四大类:
基础类型(basic type),包括数字、字符串、布尔型
聚合类型(aggregate type),包括数组、结构体
引用类型(reference type),包括指针、slice、map、函数、通道
接口类型(interface type)

1、整数

1.1 整数类型

int/uint大小与原生有符号/无符号整数相同,或等于该平台上运算效率最高的值。但即使在相同平台上,不同编译器可能使int的大小不同。
rune(int32同义词),常用于指明一个值是Unicode码点(code point)。
byte(uint8同义词),强调一个值是原始数据而非量值。
uintptr,大小不明确,但足以完整存放指针。该类型仅用于底层编程。

无符号整数往往只用于位运算和特定算法运算,如实现位集,解析二进制格式文件,散列和加密等,一般无符号整数极少用于表示非负值。

1.2 操作符与运算符

包括算术、逻辑、比较等,按优先级降序排列如下:

其中,算术运算符包括

可应用于整数、浮点数和复数,取模运算符%仅能用于整数。
取模余数的正负号总是与被除数一致。

二元比较运算符用于比较两个类型相同的整数,比较表达式本身的类型是布尔型:

全部基本类型的值(布尔值、数值、字符串)都可以比较。

一元加法与一元减法:
+ 一元取正(无实际影响)
– 一元取负
对于整数,+x是0+x的简写,-x是0-x的简写。对于浮点数和复数,+x就是x,-x就是x的负数。

位运算数:
& 按位与,
| 按位或
^ 按位异或
&^ 位清空,在z=x&^y中,若y的某位为1,则z的对应位是0,否则,等于x的对应位。
<< 左移 >> 右移

2、浮点数

Go有两种大小的浮点数:

math包给出了浮点值的极限。
常量math.MaxFloat32是float32的最大值,大约为3.4e38,
math.MaxFloat64大约为1.8e308。
十进制下,float32的有效数字大约是6位,float64的有效数字大约是15位。
绝大多数情况下,应优先使用float64。

NaN(Not a Number)表示数学上无意义的运算结果(如0/0或sqrt(-1))。

math.IsNaN函数判断其参数是否是非数值,math.NaN函数则返回非数值(NaN)。
与NaN的比较总不成立(除了!=,它总与==相反)

3、复数

Go有两种大小的复数:

二者分别由float32和float64构成。
内置的complex函数根据给定的实部和虚总创建复数,
内置的real函数和imag函数分别提取复数中的实部和虚部。
一个复数声明:

等价于:

两个复数的实部与虚部都相等,则它们相等,可以用==或!=进行判断。
math/cmplx包提供了复数运算所需的库函数。

4、布尔值

boot型的值只有两种可能:真(true)或假(false)。

布尔值可以由运算符&& 和||组合运算,这种组合运算可能引起短路行为:
如果左边的操作数已经可以直接能确定总体结果,则右边的操作数不会再计算。

&&的优先级高于||,但在实际编码过程中仍建议加上括号,以使逻辑更清晰。

5、字符串

字符串是不可变的字节序列,可以包含任意数据,包括0值字节。
内置的len()函数返回字符串的字节数(而不是文字符号的数目),下标访问操作s[i]表示取第i个字符,其中0 <= i < len(s)。
试图访问许可范围以外的字节会触发宕机异常。

len(s)则为11

字符串不可改变,字符串内部的数据不可修改,不允许像s[0] := ‘s’ 这种字符赋值操作。
这一点与C语言不同。

子串生成操作s[i:j]产生一个新字符串,内容取自原字符串的字节,下标从i(包含i),到j(不含j)。
结果的大小是j-i个字节。
其中i与j均可省略。省略i表示从第0个字节开始,省略j表示直到最后一个字节。
如:
s[:5]为hello
s[7:]为world
s[:]为hello,world
加号(+)可以连接两个字符串,生成一个新字符串。
子串与原字符串共用同一段底层内存,因此子串生成操作的开销低廉。

5.1 字符串字面串

字符串字面串(string literal)形式上就是带双引号的字节序列,如
“hello,世界”
Go的源文件总是按UTF-8编码,习惯上Go的字符串会按UTF-8解读。

转义序列:

原生字符串字面量形式为...,这里是反引号而不是双引号。
原生字符串字面量内,转义字符不起作用。

5.2 Unicode

Unicode囊括了案例文化所有文书体系的全部字符。
在go的术语中,这些字符记号称为文字符号(tune)。
天然适合保存单个文字符号的数据类型就是int32,所以rune类型作为int32类型的别名。

5.3 UTF-8

UTF-8以字节为单位对Unicode码点作变长编码。UTF-8是现行的一种Unicode标准。
UTF-8是前缀编码,能从左向右解码而不产生歧义,也无须超前预读。
Go的range循环也适用于字符串,按UTF-8隐式解码。对于非ASCII文字符号,下标增量大于1。

5.4 字符串与字节slice

对字符串操作特别重要的标准包:bytes, strings, strconv, unicode。
strings包提供了用于搜索、替换、比较、修整、切分与连接字符串。
bytes包用于操作字节slice([]byte类型)由于字符串不可变,因此按增量方式构建字符串会导致多次内存分配与复制,这时使用bytes.Buffer类型会更高效。
strconv包主要用于转换布尔值、整数、浮点数为与之对应的字符串形式,或者把字符串转换为这些形式。
unicode包用来差别文字符号值的特性,如IsDigit,IsLetter,IsUpper,IsLower等,每个函数以单个文字符号值作为参数,并返回布尔值。
字符串与字节slice可以相互转换:

[]byte(s)转换操作会分配新的字节数组,拷贝填入s含有的字节,并生成一个slice引用,指向整个数组。

5.5 字符串和数字的相互转换

将整数转换成字符串,可使用:
fmt.Sprintf:与C语言的sprintf相同
strconv.Itoa或strconv.ParseInt用于解释表示整数的字符串
strconv.ParseUint用于解释无符号整数。

5.6 常量

常量是一种表达式,本质上属于基本类型:布尔型、字符串或数字。
同一声明可以定义一系列常量:

对于常量操作数,所有数学运算、逻辑运算和比较运算的结果依然是常量。
常量的类型转换结果和某些内置函数的返回值,如len, cap, real, imag, complex和unsafe.Sizeof,同样是常量。

常量生成器iota会创建一系列相关值,从0开始取值,逐项加1。

每个常量都按 1 << iota赋值。

有些常量不从属于某一具体类型,共6种:
无类型布尔
无类型整数
无类型文字符号
无类型浮点数
无类型复数
无类型字符串

只有常量才可以是无类型的。
如果将无类型常量声明为变量,或在类型明确的变量赋值右方同出现无类型常量,则常量会被隐匿转换为该类型变量的类型。

6、复合数据类型

复合数据类型由基本数据类型以各种方式组合而成,包含数组、slice、map与结构体等。

6.1 数组

数组的长度固定。

没有指定值的索引位置的元素默认被赋值为该类型的零值,如整数的零值是0。
Go的内置函数len返回数组中的元素个数。
数组长度是数组类型的一部分,必须是常量表达式。
如果数组元素可比较,则数组可比较,可用==或!=来进行比较。
在Go语言中,把数组和其他类型都看成值传递,这一点与C语言不同。
因此可以显示地将数组的指针传递给函数。
由于数组长度不可变,因此除了特定情况之外外,很少使用数组,而是更多地使用slice。

6.2 slice

slice表示一个拥有相同类型元素的可变长度的序列。
slice的定义看起来像是没有长度的数组类型:

定义slice时方括号内没有任何字符。

slice无法做比较,不能用==来测试两个slice是否拥有相同的元素。
可以用标准库中高度优化的函数bytes.Equal来比较两个字节slice([]byte)。但对于其它类型比较自己写函数比较。

slice是一种轻量级的数据结构,可以用来访问数组的部分或全部元素,而这个数组称为slice的底层数组。
slice有三个属性:指针,长度,容量。
指针指向数组的第一个可以从slice中访问的元素,这个元素不一定是数组的第一个元素。
长度指slice中的元素个数,它不能超过slice的容量。
容量的大小通常是从slice的起始元素到底层数组的最后一个元素间的元素的个数。

一个底层数组可以对应多个slice。

如果slice的引用超过了被引用对象的容量,即cap(s),则会导致程序崩溃;
但如果slice的引用超过了被引用对象的长度,即len(s),则最终slice会比原slice长。

操作符s[i:j]创建了一个新的slice(和创建字符串的子符类似),其中0 <= i <= j <= cap(s),该slice引用了序列s从 i 到 j-1 索引位置的所有元素,这里的s既可以是数组或者是指向数组的指针,也可以是slice。

slice包含了指向数组元素的指针,所以将一个slice传递给函数的时候,可以在函数内部修改底层数组的元素。
创建一个数组的slice等于为数组创建了一个别名。
slice的元素是非直接的,有可能slice可以包含它自身。
对于引用类型如指针和通道,操作符==检查的是引用相等性,即它们是否指向相同的元素。
slice唯一允许的比较操作是和 nil 做比较。
如果想检查一个slice是否为空,使用len(s) == 0,而不是s == nil,因为在s != nil的情况下,slice也有可能是空:

内置函数make可以创建一个具体指定元素类型、长度和容量的slice:

内置函数append用来将元素追加到slice的后面:
s = []int{1, 2, 3, 4, 5}
s = appnd(s, 6)

6.3 map

散列表map是一个拥有键值对元素的无序集合,类似于Python中的字典。
在Go语言中,map是散列的引用,类型是map[K]V,其中K和V是字典的键和值对应的数据类型。
map中所有的键都拥有相同的数据类型,键的类型K必须是可以通过操作符==来进行比较的数据类型。

内置函数make可以用来创建一个map:

也可以使用map的字面量来新建一个带初始化键值对元素的字典:

其等价于:

因此一个新的空map也可以表示为:map[string]int{}。

map元素不是一个变量,不能获取它的地址。一个原因是map的增长可能会导致已有元素被重新散列到新的存储位置,可能使获取的地址无效。

map类型的零值是nil,即没有引用任何散列表。
len可以获取map元素的个数。
向零值map中设置元素会导致错误。在设置元素之前,必须初始化map。

通过下标方式访问map中的元素总会有值,会得到键值对应的值,如果键值不存在则得到对应类型的零值。

和slice一样,map不可比较,只能和 nil 比较。

6.4 结构体

结构体是将零个或任意多个类型的命名变量组合在一起的聚合数据类型。

每个成员都可以用点号来访问,点号也可以用在结构体指针上面。

成员变量的顺序对于结构体同一性非常重要,改变结构体的顺序相当于定义了一个新的结构体。

首字母大家的成员变量是可导出的。一个结构体可同时包含可导出的和不可导出的变量。

结构体的零值由结构体成员的零值组成。
没有任何成员变量的结构体称为空结构体,struct {}。
有些Go程序员用它来替代被当作集合使用的map中的布尔值,来强调只有健是有用的。

出于效率的考虑,大型结构体通常都使用结构体指针的方式直接传递给函数或者从函数中返回。
由于通常结构体都通过指针的方式使用,因此可以使用一种简单的方式来创建、初始化一个结构体类型的变量并获取它的地址:

其等价于:

但是像&Employee{10086, “Alice”, …}这种方式可以直接用在一个表达式中,例如函数调用中。

如果两个结构体所有成员变量都是可比较的,则两个结构体是可以通过==或!=比较的。
其中==操作符按照顺序比较两个结构体变量的成员变量。

Go语言允许定义不带名称的结构体成员,只需要指定类型即可,这种结构体成员称为“匿名成员”。
这个结构体成员的类型必须是一个命名类型或者指向命名类型的指针。

例如:

不过匿名成员没有什么快捷的初始化方式,

————————————————————

原创文章,转载请注明: 转载自孙希栋的博客

本文链接地址: 《Study Golang — (2) 基本数据》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Scroll Up