Go语言数据类型分类:基础类型、复合类型、引用类型和接口类型
基础类型:数字、字符串和布尔型
- 整型
有符号和无符号的整数运算:int8、int16、int32、int64四种不同大小的有符号整数类型
uint8、uint16、uint32、uint64四种无符号整数类型
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点,这两个名称可以互换使用
byte是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数
无符号整数类型uintptr,没有指定具体的bit大小但是足以容纳指针
在Go语言中,%取模运算符的符号和被取模数的符号总是一致的
除法运算符/的行为依赖于操作数是否全为整数
一个算术运算的结果,不管是有符号或者是无符号的,如果需要更多的bit位才能正确表示的话,就说明计算结果是溢出了。超出的高位的bit位部分将被丢弃
var i int8 = 127 fmt.Println(i, i+1, i*i)
位运算符^作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反
位操作运算符&^用于按位置零(AND NOT):如果对应y中bit位为1,表达式z = x &^ y结果z的对应的bit位为0,否则z对应的bit位等于x相应的bit位的值
左移运算符用零填充右边空缺的bit位,无符号的右移运算也是用0填充左边空缺的bit位,但是有符号数的右移运算会用符号位的值填充左边空缺的bit位
一般更倾向于使用有符号数,无符号数往往只有在位运算或其它特殊的运算场景才会使用,像bit集合、分析二进制文件格式或哈希和加密操作等,通常并不用于仅仅是表达非负数量的场合
当使用fmt包打印一个数值时,可以用%d、%o或%x参数控制输出的进制格式
fmt的两个使用技巧:通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数;
%后的#副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀
字符使用%c参数打印,或者是用%q参数打印带单引号的字符
- 浮点数
Go语言提供了两种精度的浮点数,float32和float64
浮点数的范围极限值可以在math包中找到,常量math.MaxFloat32表示float32能表示的最大数值,大约是3.4e38;对应的math.MaxFloat64常量大约是1.8e308
float32的有效bit位只有23个,其它的bit位用于指数和符号
用Printf函数的%g参数打印浮点数,将采用更紧凑的表示形式打印,并提供足够的精度
对应表格的数据,使用%e(带指数)或%f的形式打印可能更合适
NaN非数,函数math.IsNaN用于测试一个数是否是非数NaN,math.NaN则返回非数对应的值
- 复数
Go语言提供了两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度
内置的complex函数用于构建复数,内建的real和imag函数分别返回复数的实部和虚部
math/cmplx包提供了复数处理的许多函数,例如求复数的平方根函数和求幂函数
fmt.Println(cmplx.Sqrt(-1))
- 布尔型
一个布尔类型的值只有两种:true和false
布尔值可以和&&和||操作符结合,并且有短路行为
&&的优先级比||高
- 字符串
一个字符串是一个不可改变的字节序列
非ASCII字符的UTF8编码会要两个或多个字节
不变性意味着如果两个字符串共享相同的底层数据的话也是安全的,这使得复制任何长度的字符串代价是低廉的。同样,一个字符串s和对应的子字符串切片s[7:]的操作也可以安全地共享相同的内存,因此字符串切片操作代价也是低廉的。在这两种情况下都没有必要分配新的内存
Go语言源文件总是用UTF8编码,并且Go语言的文本字符串也以UTF8编码的方式处理,因此可以将Unicode码点也写到字符串面值中
??
一个原生的字符串面值形式是`...`,使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行(译注:在原生字符串面值内部是无法直接写`字符的,可以用八进制或十六进制转义或+"`"链接字符串常量完成)
Unicode每个符号都分配一个唯一的Unicode码点,Unicode码点对应Go语言中的rune整数类型,rune是int32等价类型
UTF8是一个将Unicode码点编码为字节序列的变长编码
??Unicode和UTF8
标准库中strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能
bytes包也提供了很多类似功能的函数
strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换
unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,它们用于给字符分类。每个函数有一个单一的rune类型的参数,然后返回一个布尔值。ToUpper、ToLower
basename实现
package main func basename(s string) string { for i:=len(s)-1;i>=0;i--{ if s[i] == '/' { s = s[i+1:] break } } for i:=len(s)-1;i>=0;i--{ if s[i] == '.' { s = s[:i] break } } return s }
package main import "strings" func basename2(s string) string { slash := strings.LastIndex(s, "/") s = s[slash+1:] if dot := strings.LastIndex(s, "."); dot>=0 { s = s[:dot] } return s }
字节slice是可变的[]byte类型,将一个字节slice转到字符串的操作是构造一个字符串拷贝,以保证复制后的字符串是只读的
为了避免转换中不必要的内存分配,bytes包和strings同时提供了许多实用函数。
strings包中的六个函数:func Contains(s, substr string) bool
func Count(s, sep string) int
func Fields(s string) [] string
func HasPrefix(s, prefix string) bool
func Index(s, sep string) int
func Join(a []string, sep string) string
bytes包中也对应的六个函数:
func Contains(b, subslice []byte) bool
func Count(s, sep []byte) int
func Fields(s []byte) [][]byte
func HasPrefix(s, prefix []byte) bool
func Index(s, sep []byte) int
func Join(s [][]byte, sep []byte) []byte
bytes包还提供了Buffer类型用于字节slice的缓存。一个Buffer开始是空的,但是随着string、byte或[]byte等类型数据的写入可以动态增长,一个bytes.Buffer变量并不需要初始化,因为零值也是有效的
当向bytes.Buffer添加任意字符的UTF8编码时,最好使用bytes.Buffer的WriteRune方法,但是WriteByte方法对于写入类似'['和']'等ASCII字符则会更加有效。
字符串和数字的转换 strconv包提供这类转换功能
将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa() 整数到ASCII
FormatInt和FormatUint函数可以用不同的进制来格式化数字
字符串到整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数
- 常量
常量表达式的值在编译期计算,而不是在运行期
所有常量的运算都可以在编译期完成,这样可以减少运行时的工作,也方便其他编译优化。当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除0、字符串索引越界、任何导致无效浮点数的操作等
iota常量生成器,用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被设置为0,然后在每一个有常量声明的行加一
无类型常量
?