Go 基本数据类型

Go 基本数据类型

author: he xiaodong date: 2020-02-05

当作笔记,原文链接

整型

int8 int16 int32 int64 uint8 uint16 uint32 uint64 int uint

Unicode 字符 rune 类型 和 int32 是等价的,通常用于表示一个 Unicode 码点。这两个名称可以互换使用。

byte 和 uint8 是等价的,byte 类型一般用于强调数值是一个原始的数据,而不是一个小的整数。

无符号的整数类型 uintptr 没有指定具体的 bit 大小,但足以容纳指针。uintptr 只有在底层编程才需要,特别是 Go 和 C 函数库或操作系统接口相交互的地方。

int 和 int32 是不同的类型,即使 int 的大小也是 32 bit。

% 取模运算符的符号和被取模数的符号一致,如 -5 % 3 和 -5 % -3 都是 -2.

位运算

& | ^ XOR 或 按位取反 « >> &^ 按位置零(位清空 AND NOT)

fmt 的两个使用技巧。通常 Printf 格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是 % 之后的 [1] 副词告诉 Printf 函数再次使用第一个操作数。第二,% 后的 # 副词告诉 Printf 在用%o、%x 或 %X 输出时生成 0、0x 或 0X 前缀。

浮点数

float32 float64 应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。因为 float32 的有效 bit 位只有23个,其它的 bit 位用于指数和符号;当整数大于23bit能表达的范围时,float32 的表示将出现误差。

%g 参数打印格式,打印形式紧凑,并提供足够精度。也可以用 %e 或 %f 。都可以控制打印宽度和精度。

函数 math.IsNaN 用于测试一个数是否是非数 NaN,math.NaN 则返回非数对应的值。虽然可以用 math.NaN 来表示一个非法的结果,但是测试一个结果是否是非数 NaN 则是充满风险的,因为NaN和任何数都是不相等的,在浮点数中,NaN、正无穷大和负无穷大都不是唯一的,每个都有非常多种的bit模式表示。

复数

complex64 complex128 分别对应 float32 float64 两种浮点数精度。 内置的 complex 函数用于构建复数,内建的 real 和 imag 函数分别返回复数的实部和虚部。

如果一个浮点数或一个十进制整数后面跟着一个i,例如3.141592i或2i,它将构成一个复数的虚部,复数的实部是0。

布尔型

true false 布尔型可以和 && 和 || 操作符结合,会有短路行为。

&& 的优先级高于  
字符串

内置的 len 函数返回一个字符串的字节数目,不是rune字符数目。

所以操作 s[i] 返回第 i 个字节的字节值。

第 i 个字节不一定是字符串的第 i 个字符,因为对于非 ASCII 字符的 UTF8 编码会要两个或多个字节。

  • 操作符将两个字符串连接成一个新字符串。

字符串可以用==和<进行比较;比较通过逐个字节 比较完成的

不变性意味如果两个字符串共享相同的底层数据的话也是安全的,这使得复制任何长度的字符串代价是低廉的,同样切割操作的代价也是廉价的,都没有必要分配新的内存。

Go语言源文件总是用UTF8编码,并且Go语言的文本字符串也以UTF8编码的方式处理,因此我们可以将Unicode码点也写到字符串面值中。

// 原生字符串,方便编写正则表达式
const GoUsage = `Go is a tool for managing Go source code.

Usage:
    go command [arguments]
...`

世界上每一个符号都分配一个唯一的 Unicode 码点,对应 Go 语言中的 rune 整数类型。

UTF8 是一个前缀编码,同时 UTF8 编码的顺序和 Unicode 码点的顺序一致,因此可以直接排序 UTF8 编码序列。同时因为没有嵌入的 NUL(0) 字节,可以很好地兼容那些使用 NUL 作为字符串结尾的编程语言。

unicode 包提供了诸多处理 rune 字符相关功能的函数(比如区分字母和数组,或者是字母的大写和小写转换等),unicode/utf8 包则提供了用于 rune 字符序列的 UTF8 编码和解码的功能。

Go 语言字符串面值中的 Unicode 转义字符让我们可以通过 Unicode 码点输入特殊的字符。有两种形式:\uhhhh 对应 16bit 的码点值,\Uhhhhhhhh 对应 32bit 的码点值,其中 h 是一个十六进制数字;一般很少需要使用 32bit 的形式。每一个对应码点的 UTF8 编码。

对于小于 256 码点值可以写在一个十六进制转义字节中,例如 ‘\x41’ 对应字符 ‘A’ ,但是对于更大的码点则必须使用 \u 或 \U 转义形式。因此,’\xe4\xb8\x96’ 并不是一个合法的 rune 字符,虽然这三个字节对应一个有效的 UTF8 编码的码点。

对于 UTF8 编码后文本的处理和原始的字节处理逻辑是一样的。但是对应很多其它编码则并不是这样的。(上面的函数都来自 strings 字符串处理包,真实的代码包含了一个用哈希技术优化的 Contains 实现。)

UTF8 字符串作为交换格式是非常方便的,但是在程序内部采用 rune 序列可能更方便,因为 rune 大小一致,支持数组索引和方便切割。

string 接受到 []rune 的类型转换,可以将一个 UTF8 编码的字符串解码为 Unicode 字符序列:

// "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]"

如果是将一个 []rune 类型的 Unicode 字符 slice 或数组转为 string,则对它们进行 UTF8 编码:

fmt.Println(string(r)) // "プログラム"

将一个整数转型为字符串意思是生成以只包含对应Unicode码点字符的UTF8字符串;

fmt.Println(string(65))     // "A", not "65"
fmt.Println(string(0x4eac)) // "京"

如果对应码点的字符是无效的,则用 ‘\uFFFD’ 无效字符作为替换:

fmt.Println(string(1234567)) // "�"

标准库有四个包对字符串处理尤为重要:bytes,strings,strconv,unicode。

strings 包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。

bytes 包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte 类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用 bytes.Buffer 类型将会更有效。

strconv 包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。

unicode 包提供了 IsDigit、IsLetter、IsUpper 和 IsLower 等类似功能,它们用于给字符分类。每个函数有一个单一的 rune 类型的参数,然后返回一个布尔值。而像 ToUpper 和 ToLower 之类的转换函数将用于 rune字符的大小写转换。所有的这些函数都是遵循 Unicode 标准定义的字母、数字等分类规范。strings 包也有类似的函数,它们是 ToUpper 和 ToLower,将原始字符串的每个字符都做相应的转换,然后返回新的字符串。

path 和 path/filepath 包提供了关于文件路径名更一般的函数操作,则使用操作系统本身的路径规则。

一个字符串是包含的只读字节数组,一旦创建,是不可变的。

字符串和字节 slice 之间可以相互转换:

s := "abc"
b := []byte(s)
s2 := string(b)

一个 []byte(s) 转换是分配了一个新的字节数组用于保存字符串数据的拷贝,然后引用这个底层的字节数组。

将一个字节 slice 转到字符串的 string(b) 操作则是构造一个字符串拷贝,以确保 s2 字符串是只读的。

bytes 包还提供了 Buffer 类型用于字节 slice 的缓存。当向bytes.Buffer 添加任意字符的 UTF8 编码时,最好使用 bytes.Buffer 的 WriteRune 方法。

字符串和数值之间的转换也比较常见,由 strconv 包提供这类转换功能。

常量

常量表达式的值在编译期计算。

批量声明的常量,除了第一个外其他的常量右边的初始化表达式可以省略,使用前面常量的初始化表达式写法,对应的类型也一样。

iota 常量生成器

类似枚举。

type Weekday int

const (
    Sunday Weekday = iota   // 0
    Monday                  // 1 
    Tuesday                 // 2
    Wednesday               // 3
    Thursday                // 4
    Friday                  // 5
    Saturday                // 6
)
// 每个常量都是1024的幂,无类型常量
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
)
无类型常量

Go 语言的常量有个不同寻常之处。虽然一个常量可以有任意有一个确定的基础类型,例如 int 或 float64,或者是类似 time.Duration 这样命名的基础类型,但是许多常量并没有一个明确的基础类型。编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算;你可以认为至少有 256bit 的运算精度。这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。

通过延迟明确常量的具体类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。

math.Pi 是无类型的浮点数常量,可以直接用于任意需要浮点数或复数的地方。

对于常量面值,不同的写法可能会对应不同的类型。例如 0、0.0、0i 和 ‘\u0000’ 虽然有着相同的常量值,但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。同样,true和false也是无类型的布尔类型,字符串面值常量是无类型的字符串类型。

只有常量可以是无类型的。当一个无类型的常量被赋值给一个变量的时候,无类型的常量将会被隐式转换为对应的类型。

无类型的整数常量默认转换为 int,对应不确定的内存大小,但是浮点数和复数常量则默认转换为 float64 和 complex128