第3章 运算符 在Go语言中,运算符(Operator)是一种特殊的符号,它表示应该执行何种计算。运算 符作用的对象称为操作数(Operand)。 var a int = 1 var b int = 2 fmt.Println(a + b) //"+"是运算符,变量a 和b 是操作数 用运算符和括号将操作数连接起来的、符合Go 语法规范的式子,称为表达式 (Expression),如a+ b-5。一个单独的常量或变量也是表达式。Go语言的内置运算符 有算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。 3.1 算术运算符 Go语言中一共有9个算术运算符,如表3-1所示。 表3-1 算术运算符 运算符类 别意 义示 例说 明 + 一元正号+a 正号 + 二元求和a+ b 计算a与b的和 - 一元负号-a 负号 - 二元求差a- b 计算a与b的差 * 二元乘法a* b 计算a与b的积 / 二元除法a/b 计算a除以b的商 % 二元求余a% b 计算a除以b的余数 ++ 一元自增a++ 等价于a=a+1 -- 一元自减a-- 等价于a=a-1 举例: func main() { var a, b int = 5, 3 fmt.Println("+a", +a) //输出+a 5 第3章 运算符 27 fmt.Println("a + b", a+b) //输出a + b 8 fmt.Println("-a", -a) //输出-a -5 fmt.Println("a - b", a-b) //输出a - b 2 fmt.Println("a * b", a*b) //输出a * b 15 fmt.Println("a / b", a/b) //输出a / b 1 fmt.Println("a % b", a%b) //输出a % b 2 a++ fmt.Println("a", a) //输出a 6 b-- fmt.Println("b", b) //输出b 2 } n% m 的计算结果是[0,m-1]。求余运算的一个应用场景是求一个多位数各个数位 上的数字。 func main() { var num int = 351 fmt.Println(num / 100) //得到num 百位上的数字3 fmt.Println(num / 10 % 10) //得到num 十位上的数字5 fmt.Println(num % 10) //得到num 个数上的数字1 } 两个整数相除的结果为整数,而不是浮点数,这与C语言相同,如5/2=2,而不是2.5。 表3-2给出了两个整数的除法与求余运算的4种情况。求余运算的结果,其符号与被除数 的符号相同。 表3-2 两个整数的除法与求余运算 x y x/y x% y 5 3 1 2 -5 3 -1 -2 5 -3 -1 2 -5 -3 1 -2 3.2 关系运算符 Go语言中一共有6个关系运算符,如表3-3所示。 表3-3 关系运算符 运算符意 义示 例说 明 == 等于a== b 如果a等于b,则结果为true;否则为false != 不等于a!= b 如果a不等于b,则结果为true;否则为false < 小于a< b 如果a小于b,则结果为true;否则为false <= 小于或等于a<= b 如果a小于或等于b,则结果为true;否则为false 续表 28 Go语言程序设计教程 运算符意 义示 例说 明 > 大于a> b 如果a大于b,则结果为true;否则为false >= 大于或等于a>= b 如果a大于或等于b,则结果为true;否则为false 举例: func main() { var a, b int = 5, 3 fmt.Println("a == b", a == b) //输出a == b false fmt.Println("a != b", a != b) //输出a != b true fmt.Println("a < b", a < b) //输出a < b false fmt.Println("a <= b", a <= b) //输出a <= b false fmt.Println("a > b", a > b) //输出a > b true fmt.Println("a >= b", a >= b) //输出a >= b true } 3.3 逻辑运算符 Go语言中一共有3个逻辑运算符,如表3-4所示。 表3-4 逻辑运算符 运算符示例说 明 && a&&b 逻辑与,如果a与b都为true,则结果为true;否则为false || a||b 逻辑或,如果a与b都为false,则结果为false;否则为true ! !a 逻辑非,如果a为false,则结果为true;否则为false 举例: func main() { var a bool = true var b bool = false fmt.Println("a && b", a && b) //输出a && b false fmt.Println("a || b", a || b) //输出a || b true fmt.Println("!a", !a) //输出!a false } 在逻辑表达式的求解过程中,并不是所有的表达式都会被执行。只有在必须执行下一 个表达式才能确定其值时,才会执行这个表达式,这种特性叫作短路求值(Short-Circuit Evaluation)。计算exp1&&exp2时,如果exp1的值为false,那么就不会计算exp2的值。 因为不论exp2的值是true还是false,整个表达式的值一定是false,而不受exp2值的影响。 同理,计算exp1||exp2的值时,只要exp1的值为true,就不会再计算exp2的值。 在某些情况下,利用短路求值特性可以编写出既简洁又高效的代码。假如有两个变量 a和b,想知道表达式b/a是否大于0。考虑到a可能为0,可以这样编写代码: 第3章 运算符 29 a != 0 && (b / a) > 0 当a为0时,a!=0为false,短路求值特性确保第二个表达式(b/a)> 0不会被计 算,从而避免引发程序异常。 3.4 位运算符 位运算符将操作数看作一个二进制数字序列,并对其进行逐位操作。Go语言支持的位 运算符如表3-5所示。 表3-5 位运算符 运算符示 例意 义说 明 & a&b 按位与对应位的与运算,只有两者都是1,才为1;否则为0 | a|b 按位或对应位的或运算,只要有一个为1,则为1;否则为0 ^ ^a 按位取反将每一位都求反,如果为0,则为1;如果为1,则为0 ^ a^b 按位异或对应位的异或运算,如果两者不同,则为1;否则为0 &^ a&^b 按位清零b的某位为1时,将a中的对应位清零,其他位不变 >> a>> n 按位右移将每一位都向右移动n位 << a<< n 按位左移将每一位都向左移动n位 举例: func main() { var a uint8 = 12 //12 的二进制形式00001100 var b uint8 = 10 //10 的二进制形式00001010 fmt.Println("a & b", a&b) //输出a & b 8,8 的二进制形式1000 fmt.Println("a | b", a|b) //输出a | b 14,14 的二进制形式1110 fmt.Println("^a", ^a) //输出^a 243,243 的二进制形式11110011 fmt.Println("a ^ b", a^b) //输出a ^ b 6,6 的二进制形式110 fmt.Println("a &^ b", a&^b) // 输出a &^ b 4,4 的二进制形式100 fmt.Println("a >> 2", a>>2) / /输 出a >> 2 3,3 的二进制形式11 fmt.Println("a << 2", a<<2) / /输 出a << 2 48,48 的二进制形式110000 } 3.5 赋值运算符 在Go语言中,“=”就是赋值运算符,其作用是将“=”右侧表达式的值赋给“=”左侧的 变量,如b=a+2。在赋值运算符“=”的前面加上其他运算符,就构成了复合赋值运算 符。如果在“=”的前面添加一个“+”运算符,就构成了复合赋值运算符“+=”。 a += 1 等价于a = a + 1 a %= 3 等价于a = a % 3 a ^= 5 等价于a = a ^ 5 30 Go语言程序设计教程 a *= 5 - 2 等价于a = a * (5 - 2) // 注意,不是等价于a = a * 5 - 2 算术运算符支持这种用法: +=、-=、*=、/=和%= 举例: func main() { var a int = 2 a *= 3 - 1 //等价于a = a * (3 - 1),而不是a = a * 3 - 1 fmt.Println(a) / /输出是4,而不是5 } 位运算符也支持这种用法: &=、|=、^=、>>=和<<= 举例: func main() { var a uint8 = 2 //2 的二进制形式是00000010 a <<= 1 //等价于a = a << 1 fmt.Println("a =", a) //输出a = 4 } Go语言的其他运算符有两个:一个是用于获取变量的存储地址&;另一个是指针运算 符*。本书后面的章节再介绍这两个运算符。 3.6 运算符的优先级 求解一个表达式的值时,必须按照运算符的优先级(Preference)从高到低的顺序进行计 算(括号除外)。表3-6按照优先级从高(级别8)到低(级别1)的顺序列出Go语言的部分运 算符。 表3-6 运算符的优先级 优先级运 算 符说 明 8 !、+x、-x、^x 逻辑非、正号、负号、按位取反 7 *、/、%、<<、>>、&、&^ 乘、除、求余、左移、右移、按位与、按位清零 6 +、-、|、^ 加、减、按位或、按位异或 5 ==、!=、<、<=、>、>= 相等、不等、小于、小于或等于、大于、大于或等于 4 && 逻辑与 3 || 逻辑或 2 = 赋值符(包括复合赋值符) 1 , 逗号 第3章 运算符 31 如果两个运算符的优先级相同,则按照从左往右的顺序进行计算。如果记不清两个运 算符的优先级大小,则可以使用小括号()。 举例: func main() { var a bool a = 2 == 1+1 //等价于a = (2 == (1+1)) fmt.Println(a) / /输出true } 有时使用小括号不是为了改变运算顺序,而是为了提高代码的可读性,这是一种很好的 编程实践,减轻了程序员记忆运算符优先级的负担。 (a < 10) && (b > 20) //推荐的做法,尽管小括号是多余的 a < 10 && b > 20 //不推荐 3.7 小结 本章讲述了各种运算符的用法,包括算术运算符、关系运算符、逻辑运算符、位运算符 等。将赋值符“=”与其他运算符相结合就构成了复合赋值运算符,如“+=”。求解一个表 达式的值时,必须按照运算符的优先级从高到低的顺序进行计算(括号除外)。另外,逻辑运 算符还有一个短路求值特性,利用这一特性可以编写出更简洁、更高效的代码。 练习题 1.编程计算下列各表达式的值: (1)5+6 (2)5-6 (3)5*6 (4)10/4 (5)10%4 2.写出表达式a+ b-5的运算符和操作数。 3.执行下列代码,其输出结果是。 fmt.Println(100 - 25*3 % 4) 4.已知x=3,执行x*=2+1语句后x的值等于。 5.如果数据在计算机内存中用1字节存储,则-5的补码为。 6.代码5>3+2的执行结果是。 7.代码fmt.Println("a"<"b")的执行结果是。 8.代码fmt.Println(124+3.0)的执行结果是。 9.请举例说明短路求值特性。 10.已知x=true,y=false,求下列各表达式的值: 32 Go语言程序设计教程 (1)x||y (2)x&&y (3)!x 11.已知a=6,b=5,求下列各表达式的值: (1)a&b (2)a|b (3)a^b (4)^a (5)a>>1 (6)b<<1 12.写出下列代码的输出结果: func main() { a, b := 5, 2 c := (a != 0 && (b/a) > 0) fmt.Println(c) } 13.编写程序,输出一个三位数254的个位数字、十位数字和百位数字。 14.阅读下列代码,写出程序的输出结果: func main() { var x, y uint8 = 5, 3 fmt.Println(x & y) fmt.Println(x | y) fmt.Println(^x) fmt.Println(x ^ y) fmt.Println(x &^ y) fmt.Println(x << 1) fmt.Println(x >> 1) }