第三章 基本类型
Go语言四大数据类型:基础类型、复合类型、引用类型和接口类型
- 基础类型 数字、字符串和布尔型
- 复合类型 数组和结构体
- 引用类型 指针、切片、字典、函数、通道
- 接口类型 抽象类型
3.1整型
划重点
int8
、int16
、int32
和int64
、int
uint8
、uint16
、uint32
和uint64
、uint
- Unicode字符
rune
类型是和int32
等价的类型,通常用于表示一个Unicode码点 byte
是uint8
类型的等价类型,byte
类型一般用于强调数值是一个原始的数据而不是一个小的整数- uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方
- 将
int
当作int32
类型的地方需要一个显式的类型转换操作,反之亦然,其他的也是,及时大小一致,但是需要显示转换
var apples int32 = 1 var oranges int16 = 2 var compote int = apples + oranges // compile error
- 算术运算、逻辑运算和比较运算,优先级递减排序
* / % << >> & &^ + - | ^ == != < <= > >= && ||
%
取模运算符的符号和被取模数的符号总是一致的,因此-5%3
和-5%-3
结果都是-2
- 除法运算符
/
,5.0/4.0
的结果是1.25
,但是5/4
的结果是1
- 超出的高位的bit位部分将被丢弃。如果原始的数值是有符号类型,而且最左边的bit为是1的话,那么最终结果可能是负的
- 比较运算符
== equal to != not equal to < less than <= less than or equal to > greater than >= greater than or equal to
- bit位操作运算符
& 位运算 AND | 位运算 OR ^ 位运算 XOR &^ 位清空 (AND NOT) << 左移 >> 右移
- 无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等
- 类型转换会造成精度的丢失,和C语言类似
- fmt的使用技巧:
Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的 [1]副词告诉Printf函数再次使用第一个操作数。第二,%后的 # 副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀
o := 0666 fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666" x := int64(0xdeadbeef) fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x) // Output: // 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
ascii := 'a' unicode := '国' newline := '\n' fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'" fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 国 '国'" fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
3.2浮点数
划重点
float32
和float64
- 常量math.MaxFloat32表示float32能表示的最大数值,大约是 3.4e38
- math.MaxFloat64常量大约是1.8e308
- 最小值近似为1.4e-45和4.9e-324
- 因为
float32
的有效bit位只有23个,其它的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32
的表示将出现误差,建议使用float64
- 小数点前面或后面的数字都可能被省略(例如
.707
或1.
)。很小或很大的数最好用科学计数法书写,通过e
或E
来指定指数部分 +Inf -Inf NaN
- 在浮点数中,NaN、正无穷大和负无穷大都不是唯一的,每个都有非常多种的bit模式表示,浮点数的相等比较是危险的,需要特别小心处理精度问题
nan := math.NaN() fmt.Println(nan == nan, nan < nan, nan > nan) // "false false >false"
常用库及方法
math
math.IsNaN
math.NaN
math.Pi
math.Sin
math.Cos
math.Hypot(x, y)
3.3复数
划重点
complex64
和complex128
var x complex128 = complex(1, 2) // 1+2i var y complex128 = complex(3, 4) // 3+4i fmt.Println(x*y) // "(-5+10i)" fmt.Println(real(x*y)) // "-5" fmt.Println(imag(x*y)) // "10"
- 复数也可以用
==
和!=
进行相等比较
常用库及方法
image
image.NewRGBA
image.Rect
img.Set
``image/color
color.Color
color.Gray
color.Black
image/png
png.Encode
math/cmplx
cmplx.Sqrt(-1)
cmplx.Abs()
``
3.4布尔值
划重点
- 布尔类型的值只有两种:
true
和false
- 布尔值可以和
&&(AND)
和||(OR)
操作符结合,&&
的优先级比||
高,下面的形式不需要小括号
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { // ...ASCII letter or digit... }
- 布尔值并不会隐式转换为数字值0或1,反之亦然
3.5字符串
划重点
- 一个字符串是一个不可改变的字节序列。
- 文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。
- 内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目)。
- 索引操作
s[i]
返回第i
个字节的字节值,i
必须满足0 ≤ i< len(s)
条件约束。 - 如果试图访问超出字符串索引范围的字节将会导致panic异常。
- 第
i
个字节并不一定是字符串的第i
个字符,因为对于非ASCII字符的UTF8编码会要两个或多个字节。 +
操作符将两个字符串链接构造一个新字符串。- 字符串可以用
==
和<
进行比较;比较通过逐个字节比较完成的,因此比较的结果是字符串自然编码的顺序。 - 字符串的值是不可变的:一个字符串包含的字节序列永远不会被改变,当然我们也可以给一个字符串变量分配一个新字符串值。
- 因为字符串是不可修改的,因此尝试修改字符串内部数据的操作也是被禁止的。
s[0] = 'L' // compile error: cannot assign to s[0]
- 不变性意味如果两个字符串共享相同的底层数据的话也是安全的,这使得复制任何长度的字符串代价是低廉的。同样,一个字符串s和对应的子字符串切片s[7:]的操作也可以安全地共享相同的内存,因此字符串切片操作代价也是低廉的。在这两种情况下都没有必要分配新的内存。下图演示一个字符串和两个字串共享相同的底层数据。
- Go语言源文件总是用UTF8编码,Go语言的文本字符串也以UTF8编码的方式处理。
3.5.1字符串面值
划重点
- 转义字符
\a 响铃 \b 退格 \f 换页 \n 换行 \r 回车 \t 制表符 \v 垂直制表符 \' 单引号 (只用在 '\'' 形式的rune符号面值中) \" 双引号 (只用在 "..." 形式的字符串面值中) \\ 反斜杠
- 十六进制的转义形式是\xhh;一个八进制转义形式是\ooo
- 一个原生的字符串面值形式是 …,使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行(译注:在原生字符串面值内部是无法直接写字符的,可以用八进制或十六进制转义或
+"```"
链接字符串常量完成) - 原生字符串面值用于编写正则表达式(正则表达式会包含很多反斜杠)\HTML模板、JSON面值、命令行提示信息以及那些需要扩展到多行的场景
const GoUsage = `Go is a tool for managing Go source code. Usage: go command [arguments] ...`
3.5.2 Unicode
划重点
- Unicode,码点对应Go语言中的
rune
整数类型(译注:rune
是int32
等价类型)
3.5.3 UTF-8
划重点
- UTF8编码由Go语言之父Ken Thompson和Rob Pike共同发明的,现在已经是Unicode的标准;
- UTF8编码使用1到4个字节来表示每个Unicode码点,ASCII部分字符只使用1个字节,常用字符部分使用2或3个字节表示。如果第一个字节的高端bit是110,则说明需要2个字节;后续的每个高端bit都以10开头。
0xxxxxxx runes 0-127 (ASCII) 110xxxxx 10xxxxxx 128-2047 (values <128 unused) 1110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused) 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values >unused)
- Go语言字符串面值中Unicode码点输入特殊的字符。有两种形式:
\uhhhh
对应16bit的码点值,\Uhhhhhhhh
对应32bit的码点值,其中h
是一个十六进制数字,32bit的形式很少用。下面字符串面值表示的值相同:
"世界" "\xe4\xb8\x96\xe7\x95\x8c" "\u4e16\u754c" "\U00004e16\U0000754c"
对于字符同样适用,下面的字符是等价的:
'世' '\u4e16' '\U00004e16'
- Go语言的range循环在处理字符串的时候,会自动隐式解码UTF8字符串
- UTF8
utf8.DecodeRuneInString
解码或range
中隐式解码,遇到错误的UTF8编码,将生成一个特别的Unicode
字符'\uFFFD'
,在印刷中会显示成"�"。 string
转[]rune
得到解码的Unicode字符序列;
s := "プログラム" r := []rune(s) fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
[]rune
类型的Unicode字符slice
或数组转为string
,则为UTF-8
编码;
fmt.Println(string(r)) // "プログラム"
- 整数转为字符串是生成以只包含对应Unicode码点字符的UTF8字符;
fmt.Println(string(65)) // "A", not "65" fmt.Println(string(0x4eac)) // "京"
- 对应码点的字符是无效的,则用’\uFFFD’无效字符作为替换;
fmt.Println(string(1234567)) // "�"
常用库及方法
unicode/utf8
utf8.RuneCountInString(s)
utf8.DecodeRuneInString
utf8.EncodeRune()
utf8.DecodeRune()
3.5.4字符串和Byte切片
划重点
- 一个字符串是包含的只读字节数组,一旦创建,是不可变的。相比之下,一个字节slice的元素则可以自由地修改。两者之间的转换。
s := "abc"
b := []byte(s)
s2 := string(b)
常用库及方法
++四个重要的字符串处理包++
bytes
主要针对的类型[]byte
,和字符串结构类型相同,因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。所以bytes.Buffer
更有优势。bytes.Contains
bytes.Count
bytes.Fields
bytes.HasPrefix
bytes.Index
bytes.Join
bytes.Buffer
bytes.Buffer.WriteByte
bytes.Buffer.WriteString
bytes.Buffer.WriteRune
//对于UTF8编码bytes.Buffer.String
strings
包含查询、替换、比较、截断、拆分和合并。strings.Contains
strings.Count
strings.Fields
strings.HasPrefix
strings.Index
strings.Join
strings.LastIndex
strconv
提供布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换unicode
提供IsDigit
、IsLetter
、IsUpper
和IsLower
等类似功能,它们用于给字符分类。ToUpper
,ToLower
。path
和path/filepath
包提供了关于文件路径名更一般的函数操作。POSIX系统使用/foo/bar
,而Microsoft Windows使用c:\foo\bar
。
3.5.5 字符串和数字的转换
划重点
- 整数->字符串 1.
fmt.Sprintf
2.strconv.Itoa(“整数到ASCII”)
- 字符串->整数 1.
strconv.Atoi
3.strconv.ParseInt
fmt.Scanf
来解析输入的字符串和数字
常用库及方法
strconv
strconv.Itoa
strconv.FormatInt()
strconv.Atoi
strconv.ParseInt
fmt
fmt.Scanf
3.6常量
划重点
- 常量表达式的值在编译期计算,而不是在运行期。基础类型:boolean、string或数字。
- 常量间的所有算术运算、逻辑运算和比较运算、类型转换的结果也是常量,以及下面的方法
len、cap、real、imag、complex和unsafe.Sizeof
。 - 批量声明的常量,除第一个外其他可以省略,省略的用上一个代替。
常用库及方法
3.6.1iota 常量生成器
划重点
- 第一个
iota
将会被置为0,然后在每一个有常量声明的行加一。 - 类似于枚举
type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday )
const ( _ = 1 << (10 * iota) KiB // 1024 MiB // 1048576 GiB // 1073741824 TiB // 1099511627776 (exceeds 1 << 32) PiB // 1125899906842624 EiB // 1152921504606846976 ZiB // 1180591620717411303424 (exceeds 1 << 64) YiB // 1208925819614629174706176 )
常用库及方法
3.6.2无类型常量
划重点
- 6种无类型常量:无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。
- 延迟明确常量的具体类型,这样的好处是可以:1)保证精度;2)不需要显示的类型转换。
var x float32 = math.Pi var y float64 = math.Pi var z complex128 = math.Pi
- 对于常量面值,不同的写法可能会对应不同的类型。
0 无类型的整数 0.0 无类型的浮点数 0i 无类型的复数 '\u0000' 无类型的字符
常用库及方法