Go语言中10种不同的整数类型以及使用方法

本章学习目标

  • 学会使用10种不同的整数类型
  • 学会选择合适的类型
  • 学会使用十六进制表示和二进制表示

Go提供了10种类型用于表示整数,它们被统称为整数类型(integer)。整数类型不能存储分数,也不会出现浮点类型的精度问题,但因为每种整数类型的取值范围都各不相同,所以我们应该根据场景所需的取值范围来决定使用何种整数类型。

请考虑这一点

你可以用两个记号(token)表示多少个数字?

如果这两个记号可以按位置进行区分,那么它们将有4种可能的排列方式:两个标识都存在;两个标识都不存在;只有一个标识存在;只有另一个标识存在。这4种排列方式的每一种可以表示一个数字,因此两个记号最多可以表示4个数字。

与此类似,计算机使用二进制位表示数字,每个二进制位的值要么为1,要么为0,这两个值分别表示打开和关闭两种状态。基于上述排列原理,使用8个二进制位总共可以表示256个不同的值。按照这种方法计算,我们需要使用多少个二进制位才可以表示数字4 000 000 000?

7.1 声明整数类型变量

在Go提供的众多整数类型当中,有5种整数类型是有符号(signed)的,这意味着它们既可以表示正整数,又可以表示负整数。在这些整数类型中,最常用的莫过于代表有符号整数的int类型了:

var year int = 2018

除有符号整数之外,Go还提供了5种只能表示非负整数的无符号(unsigned)整数类型,其中的典型为uint类型:

var month uint = 2

因为Go在进行类型推断的时候总是会选择int类型作为整数值的类型,所以下面这3行代码的意义是完全相同的:

year := 2018
var year = 2018
var year int = 2018

提示 正如第6章的浮点类型例子所示,如果类型推断可以正确地为变量设置类型,那么我们就没有必要为其指定int类型。

速查7-1

如果你的水杯里面有半杯水,你会选择哪种整数类型来表示水杯中的水有多少毫升呢?

7.1.1 为不同场合而设的整数类型

无论是有符号整数还是无符号整数,它们都有各种不同大小(size)的类型可供选择,而不同大小又会影响它们自身的取值范围以及内存占用。表7-1列出了8种与计算机架构无关的整数类型,以及这些类型需要占用的内存大小。

Go语言中10种不同的整数类型以及使用方法

 

表7-1 与计算机架构无关的整数类型

Go语言中10种不同的整数类型以及使用方法

 

正如表7-1所示,Go提供了非常多的整数类型可供选择。本章稍后将会介绍其中一些类型的应用场景,并说明当程序超出类型的有效取值范围时会发生什么事情。

因为int类型和uint类型会根据目标硬件选择最合适的位长,所以它们未被包含在表7-1里面。举个例子,在诸如Go Playground、Raspberry Pi 2和旧款手机等32位架构上,int和uint都是32位值,而较新的计算机都基于64位架构,所以这些架构上的int和uint都是64位值。

提示 如果你的程序需要操作20亿以上的数值并且可能会在32位架构上运行,那么请确保你使用的是int64类型或者uint64类型,而不是int类型或者uint类型。

注意 在某些架构上把int看作int32,而在另一些架构上则把int看作int64,这是一种非常想当然的想法,但这种想法实际上并不正确:int不是其他任何类型的别名,int、int32和int64实际上是3种不同的类型。

速查7-2

哪种整数类型的值可以是–20 151 021?

7.1.2 了解类型

正如代码清单7-1所示,如果你对Go编译器推断的类型感到好奇,那么可以使用Printf函数提供的格式化变量%T去查看指定变量的类型。

代码清单7-1 检视变量的类型:inspect.go

year := 2018
fmt.Printf("Type %T for %v\n", year, year)  ←--- 打印出“Type int for 2018”

为了避免在Printf函数中重复使用同一个变量两次,我们可以将[1]添加到第二个格式化变量%v中,以此来复用第一个格式化变量的值days,从而避免代码重复:

days := 365.2425
fmt.Printf("Type %T for %[1]v\n", days)  ←--- 打印出“Type float64 for 365.2425”

速查7-3

被双引号包围的文本、整数、实数以及(没有被双引号包围的)单词true,你知道Go语言会为它们推断什么类型吗?请扩展代码清单7-1,声明多个变量并为它们分别赋予上述提到的各个值,然后执行程序,看看Go语言会为它们推断何种类型。

7.2 为8位颜色使用uint8类型

层叠样式表(CSS)技术通过范围为0~255的红绿蓝三原色来指定画面上的颜色。因为8位无符号整数正好可以表示范围为0~255的值,所以使用uint8类型来表示层叠样式表中的颜色可以说是再合适不过了:

var red, green, blue uint8 = 0, 141, 213

与最常见的int类型相比,使用uint8类型有以下好处。

  • uint8类型可以将变量的值限制在合法范围之内,与32位整数相比,uint8消除了超过40亿种可能出现的错误值。
  • 对于未压缩图片这种需要按顺序存储大量颜色的场景,使用8位整数可以节省大量内存空间。

Go语言中的十六进制数字

层叠样式表(CSS)通过十六进制数字而不是十进制数字来指定颜色。与十进制只使用10个数字相比,十六进制需要多用6个数字:其中前10个数字跟十进制一样,都是0~9,但是之后的6个数字是十六进制数字A ~ F。十六进制中的A相当于十进制中的10,B相当于11,以此类推,直到相当于15的F为止。

十进制对拥有十根手指的人类来说是一种非常棒的数字系统,但与之相比,十六进制更适合计算机。这是因为一个十六进制数字需要消耗4个二进制位,也就是半字节(nibble),而2个十六进制数字则正好需要消耗8个二进制位,也就是1字节,这也使十六进制可以非常方便地为uint8设置值。

下表展示了一些十六进制数字以及与之对应的十进制数字。

十六进制数字和十进制数字

Go语言中10种不同的整数类型以及使用方法

 

为了区分十进制数字和十六进制数字,Go语言要求十六进制数字必须带有0x前缀。作为例子,以下两行代码分别用十进制数字和十六进制数字定义了完全相同的3个变量:

var red, green, blue uint8 = 0, 141, 213
var red, green, blue uint8 = 0x00, 0x8d, 0xd5

在使用Printf函数打印十六进制数字的时候,你可以使用%x或者%X作为格式化变量:

fmt.Printf("%x %x %x", red, green, blue)  ←--- 打印出“0 8d d5”

为了输出能够完美适配层叠样式表文件的颜色的数字,我们需要用到格式化变量%02x。它跟之前介绍过的格式化变量%v和%f一样,通过数字2指定了格式化输出的最小数字数量,并通过数字0启用了格式化的零填充功能:

fmt.Printf("color: #%02x%02x%02x;", red, green, blue)  ←--- 打印出“color: #008dd5;”

速查7-4

存储一个 uint8类型的值需要用多少字节?

7.3 整数回绕

整数类型虽然不会像浮点类型那样因为舍入错误而导致不精确,但整数类型也有它们自己的问题,那就是有限的取值范围。在Go语言中,当超过整数类型的取值范围时,就会出现整数回绕(wrap around)现象。

例如,8位无符号整数uint8类型的取值范围为0~255,而针对该类型的增量操作在结果超过255时将回绕至0。作为例子,代码清单7-2就通过执行增量操作触发了有符号和无符号8位整数的回绕现象。

代码清单7-2 整数回绕:integers-wrap.go

var red uint8 = 255
red++
fmt.Println(red)   ←--- 打印出“0”

var number int8 = 127
number++
fmt.Println(number)    ←--- 打印出“-128”

7.3.1 聚焦二进制位

为了了解整数出现回绕的原因,我们需要将注意力放到二进制位上,为此需要用到格式化变量%b,它可以以二进制位的形式打印出相应的整数值。跟其他格式化变量一样,%b也可以启用零填充功能并指定格式化输出的最小长度,就像代码清单7-3所示的那样。

代码清单7-3 打印二进制位:bits.go

var green uint8 = 3
fmt.Printf("%08b\n", green)  ←--- 打印出“00000011”
green++
fmt.Printf("%08b\n", green)  ←--- 打印出“00000100”

速查7-5

使用Go Playground试验整数回绕。

1.代码清单7-2使用1作为red和number的增量,如果在执行增量运算时对这两个变量加入一个更大的数字,结果会怎么样?

2.如果在red为0或者number等于-128时对它们执行减量运算,结果又会怎么样?

3.回绕不仅会在8位整数类型中出现,还会在16位、32位以及64位整数类型中出现。请声明一个uint16类型的变量,并将该类型的最大值65535赋予该变量,然后将这个变量的值加1,看看结果会怎么样?

提示 math包定义了值为65535的常量math.MaxUint16,还有与架构无关的整数类型的最大值常量以及最小值常量。再次提醒一下,由于int类型和uint类型的位长在不同硬件上可能会有所不同,因此math包并没有定义这两种类型的最大值常量和最小值常量。

在代码清单7-3中,对green的值执行加1操作将导致1进位,而0则被留在原位,最终计算得出二进制数00000100,也就是十进制数4,这个过程如图7-1所示。

Go语言中10种不同的整数类型以及使用方法

 

图7-1 在二进制加法中对1实施进位

正如代码清单7-4以及图7-2所示,在对值为255的8位无符号整数blue执行增量运算的时候,同样的进位操作将再次出现,但这次进位跟前一次进位有一个重要的区别:对只有8位的变量blue来说,最高位进位的1将“无处容身”,并导致变量的值变为0。

代码清单7-4 二进制位在整数回绕时的状态:bits-wrap.go

var blue uint8 = 255
fmt.Printf("%08b\n", blue)  ←--- 打印出“11111111”
blue++
fmt.Printf("%08b\n", blue)  ←--- 打印出“00000000”

Go语言中10种不同的整数类型以及使用方法

 

图7-2 “无处容身”的进位

虽然回绕在某些情况下可能正好是你想要获得的状态,但是有时候也会成为问题。最简单的避免回绕的方法就是选用一种足够长的整数类型,使它能够容纳你想要存储的值。

速查7-6

哪个格式化变量可以让你以二进制形式查看整数类型变量的值?

7.3.2 避免时间回绕

基于Unix的操作系统都使用协调世界时(UTC)1970年1月1日以来的秒数来表示时间,但是这个秒数在2038年将超过20亿,也就是大致相当于int32类型的最大值。

幸运的是,虽然32位整数无法存储2038年以后的日期,但这个问题可以通过使用64位整数来解决:在任何平台上,使用int64类型和uint64类型都可以轻而易举地存储大于20亿的数字。

作为例子,代码清单7-5使用了一个超过120亿的巨大值来展示Go足以应对2038年后的日期。这段代码使用了来自time包的Unix函数,该函数接受两个int64类型的值作为参数,它们分别代表协调世界时1970年1月1日以来的秒数和纳秒数。

代码清单7-5 使用64位整数存储日期:time.go

package main

import (
    "fmt"
    "time"
)
func main() {
    future := time.Unix(12622780800, 0)
    fmt.Println(future)  ←--- 在Go Playground打印出“2370-01-01 00:00:00 +0000 UTC”
}

速查7-7

使用哪种整数类型可以避免回绕?

7.4 小结

  • 虽然int和uint是最常用的整数类型,但是在某些情况下,我们也会用到更长或者更短的类型。
  • 除非回绕正是你需要的,否则就应该谨慎地选择合适的整数类型以避免回绕。
  • 到目前为止,你已经使用了Go提供的15种数值类型中的10种,其中包括int、int8、int16、int32、int64、uint、uint8、uint16、uint32和uint64。

为了检验你是否已经掌握了上述知识,请尝试完成以下实验。

本文摘自《Go语言趣学指南

Go语言中10种不同的整数类型以及使用方法

 

Go适合各种技术水平的程序员,这对任何大型项目来说都是至关重要的。作为一种相对较为小型的语言,Go的语法极少,需要掌握的概念也不多,因此它非常适合用作初学者的入门语言。

遗憾的是,很多学习Go语言的资源都假设读者拥有C语言的工作经验,而本书的目的则在于弥补这一缺陷,为脚本使用者、业余爱好者和初学者提供一条学习Go语言的康庄大道。为了让起步的过程变得更容易一些,本书的所有代码清单和练习都可以在Go Playground里面执行,你在阅读本书的时候甚至不需要安装任何东西。

如果你曾经使用过诸如JavaScript、Lua、PHP、Perl、Python或者Ruby这样的脚本语言,那么你已经做好了学习Go的万全准备。如果你曾经使用过Scratch或者Excel的公式,或者编写过HTML,那么你毫无疑问可以像Audrey Lim在她的演讲“A Beginner’s Mind”(初学者之心)中所说的一样,选择Go作为你的第一门“真正”的编程语言。虽然掌握Go语言并不是一件容易的事情,需要相应的耐心和努力,但我们希望本书在这个过程中能够助你一臂之力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值