#4.5 基本类型和运算符 我们将在这个部分讲解有关布尔型、数字型和字符型的相关知识。 表达式是一种特定的类型的值,它可以由其它的值以及运算符组合而成。每个类型都定义了可以和自己结合的运算符集合,如果你使用了不在这个集合中的运算符,则会在编译时获得编译错误。 一元运算符只可以用于一个值的操作(作为后缀),而二元运算符则可以和两个值或者操作数结合(作为中缀)。 只有两个类型相同的值才可以和二元运算符结合,另外要注意的是,Go 是强类型语言,因此不会进行隐式转换,任何不同类型之间的转换都必须显式说明(第 4.2 节)。Go 不存在像 C 和 Java 那样的运算符重载,表达式的解析顺序是从左至右。 你可以在第 4.5.3 节找到有关运算符优先级的相关信息,优先级越高的运算符在条件相同的情况下将被优先执行。但是你可以通过使用括号将其中的表达式括起来,以人为地提升某个表达式的运算优先级。 ##4.5.1 布尔类型 bool 一个简单的例子:`var b bool = true`。 布尔型的值只可以是常量 true 或者 false。 两个类型相同的值可以使用相等 `==` 或者不等 `!=` 运算符来进行比较并获得一个布尔型的值。 当相等运算符两边的值是完全相同的值的时候会返回 true,否则返回 false,并且只有在两个的值的类型相同的情况下才可以使用。 示例: var aVar = 10 aVar == 5 -> false aVar == 10 -> true 当不等运算符两边的值是不同的时候会返回 true,否则返回 false。 示例: var aVar = 10 aVar != 5 -> true aVar != 10 -> false Go 对于值之间的比较有非常严格的限制,只有两个类型相同的值才可以进行比较,如果值的类型是接口(interface,第 11 章),它们也必须都实现了相同的接口。如果其中一个值是常量,那么另外一个值的类型必须和该常量类型相兼容的。如果以上条件都不满足,则其中一个值的类型必须在被转换为和另外一个值的类型相同之后才可以进行比较。 布尔型的常量和变量也可以通过和逻辑运算符(非 `!`、和 `&&`、或 `||`)结合来产生另外一个布尔值,这样的逻辑语句就其本身而言,并不是一个完整的 Go 语句。 逻辑值可以被用于条件结构中的条件语句(第 5 章),以便测试某个条件是否满足。另外,和 `&&`、或 `||` 与相等 `==` 或不等 `!=` 属于二元运算符,而非 `!` 属于一元运算符。在接下来的内容中,我们会使用 T 来代表条件符合的语句,用 F 来代表条件不符合的语句。 Go 语言中包含以下逻辑运算符: 非运算符:`!` !T -> false !F -> true 非运算符用于取得和布尔值相反的结果。 和运算符:`&&` T && T -> true T && F -> false F && T -> false F && F -> false 只有当两边的值都为 true 的时候,和运算符的结果才是 true。 或运算符:`||` T || T -> true T || F -> true F || T -> true F || F -> false 只有当两边的值都为 false 的时候,或运算符的结果才是 false,其中任意一边的值为 true 就能够使得该表达式的结果为 true。 在 Go 语言中,&& 和 || 是具有快捷性质的运算符,当运算符左边表达式的值已经能够决定整个表达式的值的时候(&& 左边的值为 false,|| 左边的值为 true),运算符右边的表达式将不会被执行。利用这个性质,如果你有多个条件判断,应当将计算过程较为复杂的表达式放在运算符的右侧以减少不必要的运算。 利用括号同样可以升级某个表达式的运算优先级。 在格式化输出时,你可以使用 `%t` 来表示你要输出的值为布尔型。 布尔值(以及任何结果为布尔值的表达式)最常用在条件结构的条件语句中,例如:if、for 和 switch 结构(第 5 章)。 对于布尔值的好的命名能够很好地提升代码的可读性,例如以 `is` 或者 `Is` 开头的 `isSorted`、`isFinished`、`isVisivle`,使用这样的命名能够在阅读代码的获得阅读正常语句一样的良好体验,例如标准库中的 `unicode.IsDigit(ch)`(第 4.5.5 节)。 ##4.5.2 数字类型 ###4.5.2.1 整型 int 和浮点型 float Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码(二的补码,详情参见:[http://en.wikipedia.org/wiki/Two's_complement](http://en.wikipedia.org/wiki/Two's_complement))。 Go 也有基于架构的类型,例如:int、uint 和 uintptr。 这些类型的长度都是根据运行程序所在的操作系统类型所决定的: - `int` 和 `uint` 在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。 - `uintptr` 的长度被设定为足够存放一个指针即可。 Go 语言中没有 float 类型。 与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来: 整数: - int8(-128 -> 127) - int16(-32768 -> 32767) - int32(-2,147,483,648 -> 2,147,483,647) - int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807) 无符号整数: - uint8(0 -> 255) - uint16(0 -> 65,535) - uint32(0 -> 4,294,967,295) - uint64(0 -> 18,446,744,073,709,551,615) 浮点型(IEEE-754 标准): - float32(+- 1e-45 -> +- 3.4 * 1e38) - float64(+- 5 * 1e-324 -> 107 * 1e308) int 型是计算最快的一种类型。 整型的零值为 0,浮点型的零值为 0.0。 float32 精确到小数点后 7 位,float64 精确到小数点后 15 位。由于精确度的缘故,你在使用 `==` 或者 `!=` 来比较浮点数时应当非常小心。你最好在正式使用前测试对于精确度要求较高的运算。 你应该尽可能地使用 float64,因为 `math` 包中所有有关数学运算的函数都会要求接收这个类型。 你可以通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF),以及使用 e 来表示 10 的连乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)。 你可以使用 `a := uint64(0)` 来同时完成类型转换和赋值操作,这样 a 的类型就是 uint64。 Go 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用,下面这个程序很好地解释了这个现象(该程序无法通过编译): Example 4.8 [type_mixing.go](examples/chapter_4/type_mixing.go) package main func main() { var a int var b int32 a = 15 b = a + a // 编译错误 b = b + 5 // 因为 5 是常量,所以可以通过编译 } 如果你尝试编译该程序,则将得到编译错误 `cannot use a + a (type int) as type int32 in assignment`。 同样地,int16 也不能够被隐式转换为 int32。 下面这个程序展示了通过显示转换来避免这个问题(第 4.2 节)。 Example 4.9 [casting.go](examples/chapter_4/casting.go) package main import “fmt” func main() { var n int16 = 34 var m int32 // compiler error: cannot use n (type int16) as type int32 in assignment //m = n m = int32(n) fmt.Printf(“32 bit int is: %d\n”, m) fmt.Printf(“16 bit int is: %d\n”, n) } // the output is: 32 bit int is: 34 16 bit int is: 34 **格式化说明符** 在格式化字符串里,`%d` 用于格式化整数(`%x` 和 `%X` 用于格式化 16 进制表示的数字),`%g` 用于格式化浮点型(`%f` 输出浮点数,`%e` 输出科学计数表示法),`%0d` 用于规定输出定长的整数,其中开头的数字 0 是必须的。 `%n.mg` 用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f,例如:使用格式化字符串 `%5.2e` 来输出 3.4 的结果为 `3.40e+00`。 **数字值转换** 当进行类似 `a32bitInt = int32(a32Float)` 的转换时,小数点后的数字将被丢弃。这种情况一般发生当从取值范围较大的类型转换为取值范围较小的类型时,或者你可以写一个专门用于处理类型转换的函数来确保没有发生精度的丢失。下面这个例子展示如何安全地从 int 型转换为 int8: func Uint8FromInt(n int) (uint8, error) { if 0 <= n && n <= math.MaxUint8 { // conversion is safe return uint8(n), nil } return 0, fmt.Errorf(“%d is out of the uint8 range”, n) } 或者安全地从 float64 转换为 int: func IntFromFloat64(x float64) int { if math.MinInt32 <= x && x <= math.MaxInt32 { // x lies in the integer range whole, fraction := math.Modf(x) if fraction >= 0.5 { whole++ } return int(whole) } panic(fmt.Sprintf(“%g is out of the int32 range”, x)) } 不过如果你实际存的数字超出你要转换到的类型的取值范围的话,则会引发 panic(第 13.2 节)。 **问题 4.1: int 和 int64 是相同的类型吗?** ##啊哦,亲,你看得也太快了。。。还没翻译完呢 0 0 要不等到 ***2013 年 5 月 20 日*** 再来看看吧~~ 这里还有一些其它的学习资源噢~ - [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming):已更新至 [第10课](https://github.com/Unknwon/go-fundamental-programming/blob/master/lecture10/lecture10.md) - [《Go Web编程》](https://github.com/astaxie/build-web-application-with-golang) 神马?你说你不想学习?那好吧,去逛逛看看行情也行~ - [Go Walker](http://gowalker.org) **Go 项目文档在线浏览工具** - [Golang中文社区](http://bbs.mygolang.com/forum.php) - [Go语言学习园地](http://studygolang.com/) - [Golang中国](http://golang.tc) ###4.5.2.2 复数 ##链接 - [目录](directory.md) - 上一节:[变量](04.4.md) - 下一节:[字符串](04.6.md)