第三章 基础数据类型
Go语言将数据类型分为四类:
-
基础类型
- 数字
- 整数
- 浮点数
- 复数
- 字符串
- 布尔
- 数字
-
复合类型
- 数据
- 结构体
-
引用类型
- 指针
- 切片
- 字典
- 函数
- 通道
- 接口类型
在此章节中先介绍基础类型。
3.1 整型
Go语言中提供了有符号数和无符号数两种整型运算。
有符号数有:int8, int16, int32, int64
无符号数有:uint8, uint16, uint32, uint64
此外还有两种针对特定CPU平台机器字大小的有符号数int和无符号数uint, 它们的大小与硬件平台有关。
rune类型与int32等价
byte类型与uint8等价
一般使用byte类型强调数值是一个原始数据,而并非是一个小整数。
int, uint, int32他们属于不同的类型,不能直接进行操作(需要强转)。
Go语言中有关算数运算、逻辑运算、比较运算的二元运算符,它们按照递减顺序排列如下:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
* | / | % | << | >> | & | &^ |
---|---|---|---|---|---|---|
+ | - | \ | ^ | |||
== | != | < | <= | > | >= | |
&& | ||||||
|| |
二元运算符有五种优先级(见上图),同一优先级之间,使用左优先结合的原则,但是可以使用括号来提升优先级。例如 mask & 1 << 28, 因为& <<属于相同优先级,因此先计算mask&1, 然后再将其结果左移28位。我们可以使用括号来提升优先级:
mask & (1 << 28)算术运算符+、-、*和/ 可以适用于整数、浮点数、复数运算,但是取模运算符%只能用于整数之间
在Go语言中,%取模运算的符号与被取模的符号数总是一致的。例如
-5%3=-2
,-5%-3=-2
-
两个相同的整数类型可以使用下面的二元比较运算符进行比较,其结果为布尔型:
- ==
- !=
- <
- <=
- >
- >=
在Go中,有两个一元运算符:
+ 一元加法
- 负数
对于整数,+x是0+x的简写; -x是0-x的简写;
对于浮点数和复数,+x就是x;-x就是x的复数。
Go语言中还提供了bit位操作运算符,前四个不区分有符号数还是无符号数:
& 位运算AND
| 位运算OR
^ 位运算XOR
&^ 位清空(AND NOT)
<< 左移
>> 右移
位运算^ 作为二元运算符时按位异或;当作为一元运算符时表示按位取反
移位操作(如x<<n),n要求必须为无符号数,x可以是有符号数也可以是无符号数。左移运算,使用0填充右边空缺Bit位;无符号数右移也是用0填充左边的空缺位,但是有符号数右移则会使用符号位填充左边的空缺位(负数右移仍然为负数)。
func main() {
var i int8 = -64
fmt.Println(i)
fmt.Println(i >> 2)
fmt.Println(i >> 4)
fmt.Println(i >> 5)
fmt.Println(i >> 6)
fmt.Println(i >> 10)
fmt.Printf("%09b\n", i)
fmt.Printf("%09b\n", i>>2)
fmt.Printf("%09b\n", i>>4)
fmt.Printf("%09b\n", i>>5)
fmt.Printf("%09b\n", i>>6)
fmt.Printf("%09b\n", i>>10)
}
结果:
-64
-16
-4
-2
-1
-1
-01000000
-00010000
-00000100
-00000010
-00000001
-00000001
强制类型转换:
func main() {
var apples int32 = 1
var oranges int16 = 2
var compote int = apples + oranges
}
如果未做转换,则无法编译通过,提示如下错误:
# command-line-arguments
section3\forceConv.go:7:27: invalid operation: apples + oranges (mismatched types int32 and int16)
而比较常用的解决办法是:类型强制转换. 之后程序便可以成功编译运行。
var compote int = int(apples) + int(oranges)
同其他语言一样,在高精度向低精度类型强制转换过程中存在丢失精度的问题
关于使用Printf函数的注意事项:
func main() {
x := 100
fmt.Printf("%d %[1]x %#[1]x %#[1]b\n", x)
}
执行结果如下:
100 64 0x64 0b1100100
可以看出:
- 在%之后的[1]用来告诉Printf函数再次使用第一个操作数
- 在%之后的#用来告诉Printf函数在使用%o、%x、%X等输出时添加0、0x、0X的前缀。
- 字符使用%c参数打印,或者是用%q参数打印带单引号的字符
这两个东西都比较好用…切记切记
3.2浮点类型
Go语言提供了两种精度的浮点数,float32和float64。
file:///F:/golang/Bible/section3/surface.xml
3.3 复数
Go语言提供了两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度。
内置的complex函数用于构建复数,内建的real和imag函数分别返回复数的实部和虚部:
func main() {
var x complex128 = complex(1, 2)
var y complex128 = complex(2, 3)
/*复数相乘是交叉相乘,类似于(a+b) * (c+d) */
fmt.Println(x * y)
fmt.Println(real(x * y))
fmt.Println(imag(x * y))
fmt.Println(1i * 1i)
fmt.Println(cmplx.Sqrt(-1))
}
执行结果如下:
(-4+7i)
-4
7
(-1+0i)
(0+1i)
3.4 布尔型
一个布尔类型的值只有两种:true和false。
if和for语句的条件部分都是布尔类型的值,并且==和<等比较操作也会产生布尔型的值。
- &&运算符的优先级高于||, 因此下面的代码没有问题
if 'a' <= c && c <= 'z' ||
'A' <= c && c <= 'Z' ||
'0' <= c && c <= '9' {
/* c is letter or digit */
}
- [x]布尔值不会隐式转换为0或者1,反之亦然。两者之间需要做显式转换
func itob(i int) bool {
return i != 0
}
func btoi(b bool) int {
if b {
return 1
}
return 0
}
3.5字符串
- 在Go中,字符串是一个常量,不可修改的。
- 内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目)
- 越界访问会导致panic异常
- 子字符串操作s[i:j]基于原始的s字符串的第i个字节开始到第j个字节(并不包含j本身)生成一个新字符串
- 不管i还是j都可能被忽略,当它们被忽略时将采用0作为开始位置,采用len(s)作为结束的位置。
字符串操作demo:
func main() {
s := "hello, world!!!"
fmt.Println(len(s))
fmt.Printf("s[0]= %[1]c %[1]d\n", s[0])
fmt.Printf("s[7]= %[1]c %[1]d\n", s[7])
//fmt.Println(s[len(s)]) /*index out of range [15] with length 15*/
/*支持索引访问,如果超出范围仍然会有panic异常*/
fmt.Println(s[2:8])
fmt.Println(s[:8])
fmt.Println(s[2:])
fmt.Println(s[:])
/*可以用来拼接字符串*/
fmt.Println("Oh,shit" + s[5:])
t := s
s += "Oh,Ye!!! Oh Ye"
/*t没有变化,s字符串被追加了一段*/
fmt.Println(t)
fmt.Println(s)
s[0] = 'H' //cannot assign to s[0]
}
3.6 常量
- 常量表达式的值在编译期计算,而不是在运行期。
- 常量的值不可修改,这样可以防止在运行期被意外或恶意的修改。
- 和变量声明一样,可以批量声明多个常量;这比较适合声明一组相关的常量:
const (
e = 2.71828182845904523536028747135266249775724709369995957496696763
pi = 3.14159265358979323846264338327950288419716939937510582097494459
)
- 如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的, 详见下面例子:
func main() {
const (
a = 1
b
c = 2
d
)
fmt.Println(a) //1
fmt.Println(b) //1
fmt.Println(c) //2
fmt.Println(d) //2
}
这个是itoa常量生成器的一个前提。
3.6.1 itoa常量生成器
常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行itoa值加一。
例如:
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
func main() {
fmt.Println(Sunday) //0
fmt.Println(Monday) //1
fmt.Println(Tuesday) //2
fmt.Println(Wednesday) //3
fmt.Println(Thursday) //4
fmt.Println(Friday) //5
fmt.Println(Saturday) //6
}
下面是一个更复杂的例子,每个常量都是1024的幂
const (
_ = 1 << (10 * iota)
KiB // 1024
MiB // 1048576
GiB // 1073741824
TiB // 1099511627776 (exceeds 1 << 32)
PiB // 1125899906842624EiB // 1152921504606846976
ZiB // 1180591620717411303424 (exceeds 1 << 64)
YiB // 1208925819614629174706176
)
不过iota常量生成规则也有其局限性。例如,它并不能用于产生1000的幂(例如KB、MB等)
3.6.2 无类型常量
Go语言的常量有个不同寻常之处:存在无类型的常量,且精度更高。
许多常量并没有一个明确的基础类型。编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算
只有常量可以是无类型的。