Language GO III

本节讲述GO语言的基础数据类型


3.1. 整型

       Go语言同时提供了有符号无符号类型的整数运算。有符号整数为int8、int16、int32和int64,对应8、16、32、64bit数值。无符号为:uint8、uint16、uint32和uint64。

       最常用的关键字就是int和uint,因为不同platform的不同compiler可能对应32bit或者64bit。

       Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。byte类型等价于uint8类型,但byte类型一般用于强调数值是一个原始的数据而不是一个小的整数

      最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针,用于底层编程的接口。

     虽然在某些情况下,int和int32同为32bit,但是他们是不同类型,同理第二章的摄氏度华氏度类型,需要一个显式转换才能将int当作int32,反之同理。

      有符号整数采用补码来表示数字大小,首位作为符号位,当首位为0时,其他为照常计算,当首位为1时,第一位为本位数字的负数,与其他位相机合并计算,故8bit的数字的值域为-128(10000000)到127(01111111)。而无符号8bit整数的大小为0到255,即8位二进制的最大值,11111111。

      下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照优先级递减的顺序排列:

*      /      %      <<       >>     &       &^
+      -      |      ^
==     !=     <      <=       >      >=
&&
||

       具体的运算原则基本和C语言一致,不再赘述。

        以下代码展示了溢出,因为unit8的最大值就是255,所以再次基础上再进行加法乘法计算就会导致计算结果溢出,具体原理要查看二进制补码计算的一些规则,此处不再赘述。

var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"

var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1"

      以下是逻辑(布尔)运算符:

==    等于
!=    不等于
<     小于
<=    小于等于
>     大于
>=    大于等于

       以下是一元计算:

+      一元加法 (无效果) // “+”在只有一个var的情况下是无作用的
-      负数         // “-”代表了负数

      以下是bit运算:

&      位运算 AND
|      位运算 OR
^      位运算 XOR
&^     位清空 (AND NOT)
<<     左移
>>     右移

        位操作运算符^作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反位操作运算符&^用于按位置零(AND NOT),与非相与。

       下面的代码演示了如何使用位操作解释uint8类型值的8个独立的bit位。它使用了Printf函数的%b参数打印二进制格式的数字;其中%08b中08表示打印至少8个字符宽度,不足的前缀部分用0填充。

var x uint8 = 1<<1 | 1<<5
var y uint8 = 1<<1 | 1<<2

fmt.Printf("%08b\n", x) // "00100010", the set {1, 5}
fmt.Printf("%08b\n", y) // "00000110", the set {1, 2}

fmt.Printf("%08b\n", x&y)  // "00000010", the intersection {1}
fmt.Printf("%08b\n", x|y)  // "00100110", the union {1, 2, 5}
fmt.Printf("%08b\n", x^y)  // "00100100", the symmetric difference {2, 5}
fmt.Printf("%08b\n", x&^y) // "00100000", the difference {5}

for i := uint(0); i < 8; i++ {
    if x&(1<<i) != 0 { // membership test
        fmt.Println(i) // "1", "5"
    }
}

fmt.Printf("%08b\n", x<<1) // "01000100", the set {2, 6}
fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}

        在x<<nx>>n移位运算中,在n这个位置上的数必须是无符号数;被操作的x可以是有符号数或无符号数。实际上,一个二进制运算中,x<<n左移运算等价于乘以2的n次方,一个x>>n右移运算等价于除以2的n次方。

       左移运算用0补充新多出的bit位,无符号数的右移运算也是用0补充新多出的bit位,但是有符号数的右移运算会用符号位的值填充左边空缺的bit位。

       虽然Go语言有无符号运算,但是实际上,日常使用中仍然是有符号运算占据主流。比如,内置的len函数返回一个有符号的int,我们可以像下面例子那样处理逆序循环。

medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {
    fmt.Println(medals[i]) // "bronze", "silver", "gold"
}

        如若使用无符号返回值进行上述运算,则会出现问题。因为,如果len函数返回一个无符号数,那么i会是uint类型,然后条件i >= 0则永远为真。

       正因如此,无符号运算只在少部分特殊情况中使用。

        在很多场景,会遇到类似下面代码的常见的错误:

var apples int32 = 1
var oranges int16 = 2
var compote int = apples + oranges // compile error

       当尝试编译这三个语句时,将产生一个错误信息:

invalid operation: apples + oranges (mismatched types int32 and int16)

      这种类型不匹配的问题可以有几种不同的方法修复,将其转换为同一种类型是一个好方法:

var compote = int(apples) + int(oranges)

     将浮点数转换为整数也是可以被操作的,只是会导致一些数据的丢失:

f := 3.141 // a float64
i := int(f)
fmt.Println(f, i) // "3.141 3"
f = 1.99
fmt.Println(int(f)) // "1"
f := 1e100  // a float64
i := int(f) // 结果依赖于具体实现

        当使用fmt包打印一个数值时,我们可以用%d、%o或%x参数控制输出的进制格式,就像下面的例子:

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

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

       字符面值通过一对单引号直接包含对应字符。最简单的例子是ASCII中类似'a'写法的字符面值,但是我们也可以通过转义的数值来表示任意的Unicode码点对应的字符,马上将会看到这样的例子。

       字符使用%c参数打印,或者是用%q参数打印带单引号的字符:

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'"

Comments

Popular posts from this blog

托福 TPO词汇题汇总

浮点数

缓存