第三章 基础数据类型
整型
- int和uint都是对应特定cpu平台机器字大小的
- Unicode字符rune类型和int32等价
- byte和uint8等价
- 无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针
- 有符号整数采用2的补码形式表示
运算
== equal to
!= not equal to
< less than
<= less than or equal to
> greater than
>= greater than or equal to
+ 一元加法 (无效果)
- 负数
& 位运算 AND
| 位运算 OR
^ 位运算 XOR
&^ 位清空 (AND NOT)
<< 左移
>> 右移
- 位操作一般选用无符号数,原因在于有符号数用补码表示,位移操作后,数值往往不是达不到预期
- 一个x<<n左移运算相当于乘以2 的n次方,一个x>>n右移运算相当于除以2的n次方
- 无符号数左移或者右移,空缺的bit位将用0填充;有符号数左移后,空缺位由0填充,但是右移后,空缺位用符号位的值填充
- 有时候最好还是使用有符号数为好
- 像内置的len函数,返回值是int而不是uint
medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {
fmt.Println(medals[i]) // "bronze", "silver", "gold"
}
- 上述例子中,如果len返回的是无符号数,那么在循环的最后,i--并不会产生-1,这会产生panic
- 进行算数和逻辑运算的二元操作中,必须是相同的类型
- 类型转换中,一个大尺寸的整型转为一个小尺寸的整型,或者浮点数转为整型(一般来说丢失小数部分),可能会丢失精度
格式
- 八进制:0开始,如0666,通常用于POSIX操作系统上的文件访问权限
- 十六进制:0x开始,如0xdeadbeef,更签掉数字值的bit位模式
- 使用fmt包打印一个数值时,可以用%d(十进制),%o(八进制),%x(十六进制)参数控制输出的进制格式
- %c一个字符,%q带单引号的字符
浮点数
- GO提供了两种浮点数:float32,float64
- 按IEEE754浮点数国际标准定义
- 浮点数最值:
1.math.MaxFloat32: 3.4e38
2.math.MaxFloat64: 1.8e308
3.float32最小值:1.4e-45
4.float64最小值:4.9e-324
- 特殊值(math包提供):
1.正无穷大:表示太大溢出的数字
2.负无穷大:表示除零的结果
3.NaN:非数,注意它不等于任何数(包括它自己)
//注意!!!,都是小写开头,这些是不可导出的
//以下是源码中的部分内容
const (
uvnan = 0x7FF8000000000001
uvinf = 0x7FF0000000000000
uvneginf = 0xFFF0000000000000
)
//获取一个NaN的数
nan := math.NaN()
- 当需要精确计算的时候,必须小心误差,有些数float32和float64表达不了
格式
- fmt中的Printf函数,%g参数打印浮点数,会更加紧凑,并提供足够的精度
- %e(带指数) 和 %f 也可以,两者可以指定打印的宽度和精度
复数
- 提供了两种:complex64和complex128,分别对应float32和float64
- 几个内置函数:
1.complex:构建复数
2.real:返回复数的实部
3.imag:返回复数的虚部
- math/cmplx提供了许多复数操作
布尔型
- 两个值:true false
- 逻辑运算(&&,||等)中,可能会有短路行为:如果运算符左边值已经可以确定整个布尔表达式的值,那么运算符右边的值将不被求值
- &&的优先级比||高
字符串
- 文本字符串通常被解释为UTF8编码的Unicode码点(rune)序列
- 内置len函数,返回字符串的字节数目(不是rune字符数目),对于ascii字符是安全的,但中文就需要特别注意
- 字符串可以用==和<进行比较:逐个字节比较
- 字符串的值不可变
字符串面值
const GoUsage = `Go is a tool for managing Go source code.
Usage:
go command [arguments]
...`
Unicode
- Unicode收集了世界上所有的符号系统,uinicode码点对应Go中的rune类型(int32)
UTP-8
- UTP-8是Unicode的一种编码形式,它是一个将Unicode码点编码为字节序列的变长编码,由Go语言之父(有两个人)发明
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)
- 对于UTF8编码后文本的处理和原始字节处理逻辑是一样的,但其它编码规则不是
- 在处理中英文混合的文本时,或者说UTF8编码的文本时,可以使用unicode/utf8包中提供各种功能
import "unicode/utf8"
s := "Hello, 世界"
fmt.Println(len(s))
// "13"
fmt.Println(utf8.RuneCountInString(s)) // "9"
for i := 0; i < len(s); {
r, size := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%d\t%c\n", i, r)
i += size
}
- 而Go语言中的range循环,会自动隐式解码UTP8字符串,所以每次的步长并不一定相同
- 需要特别注意的是,如果是错误的UTF8编码,将会生成一个特别的Unicode字符'\uFFFD',表现形式为:�
- string接受[]rune的类型转换
// "program" in Japanese katakana
s := "プログラム"
fmt.Printf("% x\n", s) // "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"
r := []rune(s)
fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
字符串和Byte切片
- 标准库对字符串处理的包:
1.bytes:字符串是只读的,在这种情况下使用bytes.Buffer类型会更有效
2.strings
3.strconv:提供了布尔型,整型数,浮点数和对应字符串的相互转换,还有双引号转义相关的转换
4.unicode:提供了IsDigit,IsLetter,IsUpper,IsLower,它们都有一个rune类型的参数,和返回一个布尔值。
- 字符串和Byte切片可以互相转换
s := "abc"
b := []byte(s)
s2 := string(b)
- bytes包还提供了Buffer类型用于字节slcie缓存
字符串和数字的转换
- 功能由strconv包提供
- 可以使用fmt.Sprintf来格式化字符串
- 也可以用strconv.Itoa(整数到ASCII)
x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"
- FomatInt和FormatUint可以转换成不同进制格式,也可以使用fmt.Printf格式化
fmt.Println(strconv.FormatInt(int64(x), 2)) // "1111011"
s := fmt.Sprintf("x=%b", x) // "x=1111011"
- 字符串解析为整数,或者无符号数(ParseUint)
x, err := strconv.Atoi("123")
// x is an int
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits
常量
- 常量表达式的值在编译期计算
- 使用cosnt声明,初始化用=即可
- 常量间的所有算术运算逻辑运算和比较运算结果也是常量,对常量的类型转换或以下函数调用返回常量结果:
1.len
2.cap
3.real
4.imag
5.complex
6.unsafe.Sizeof
- fmt.Printf:可以通过%T参数来打印类型信息
- 批量声明常量,除了第一个外其它常量右边的初始化表达式都可以省略;如果省略,表示前面常量的初始化表达式
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
iota常量生成器
- 使用iota常量生成器初始化,会生成一组规则相似的常量,但不需要每行都写一次
- 在第一个声明的常量所在的行,iota将会被置为0,然后每一个有常量声明的行加1
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
无类型常量
- 许多常量并没有一个明确的基础类型(通过延迟明确常量的具体类型,可以提高运算精度,可以直接作用于更多的表达式而无需显式类型转换)
- 例如ZiB和YiB的值已经超出Go语言中所有整数类型可表达的范围,但它们依然是合法的常量
- 还有math.Pi是无类型的浮点数常量,可以直接用于以下表达式
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi
- 疑问:所有常量面值都是无类型的吗?
- 书中术语上用的是”无类型的整数,无类型的浮点数,无类型的复数,无类型的字符串“,个人认为无类型主要是精度上的区分,它的数据位数由实际上的常量面值,但其规则却是令人费解,而且还发现了个有趣的现象
const a = 3.4e1000
func main() {
fmt.Printf("%T\n", a)
fmt.Println(a)
//constant 3.4e+1000 overflows float64
}
- 这说明,无论常量面值多大,但最终运算结果也应该是Go有类型的量