go语言圣经第三章(读书笔记)

第三章 基础数据类型

整型

  • 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
)
  • go语言没有幂的运算符

无类型常量

  • 许多常量并没有一个明确的基础类型(通过延迟明确常量的具体类型,可以提高运算精度,可以直接作用于更多的表达式而无需显式类型转换)
  • 例如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有类型的量
posted @ 2019-07-22 15:09  DickLai  阅读(339)  评论(0)    收藏  举报