表达式

  1. 关键字
  2. 运算符
  3. 初始化

关键字

关键字是指被编程语言保留而不让编程人员作为标志符使用的字符序列。因此,关键字也称为保留字, 每种编程语言都有自己的关键字,从使用的角度看,我们可以把Go语言的25个关键字分为如下三类:

1. 用于程序声明  
2. 用于程序实体声明和定义
3. 用于程序流程控制的关键字
类别 关键字
程序声明 import, package
实体声明和定义 var,const,type,struct,func,interface,map,chan
流程控制 if,else,switch,case,fallthrough,default,for,range,continue,break,go,select,defer,goto,return

具体用法见后面章节

运算符

运算符就是用于执行特定计算或逻辑操作的符号。 Go语言中全部运算符、分隔符以及其他特殊符号列表:

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^    &^=

运算符分类:

1. 算术运算符
2. 关系运算符
3. 逻辑运算符
4. 位运算符
5. 赋值运算符
6. 其他运算符
  • 算术运算符

下面为Go语言的算术运算符,假定A值为10,B值为20

运算符 描述 实例
A+B A + B 输出结果 30
A-B A - B 输出结果 -10
A*B A * B 输出结果 200
A/B B / A 输出结果 2
B%A 求余 B % A 输出结果 0
A++ 自增 A++ 输出结果 11
A– 自减 A– 输出结果 9
  • 关系运算符

下面为Go语言的关系运算符,假定A值为10,B值为20

运算符 描述 实例
A==B 检查两个值是否相等,如果相等返回 True 否则返回 False (A == B) 为 False
A!=B 检查两个值是否不相等,如果不相等返回 True 否则返回 False (A != B) 为 True
A>B 检查左边值是否大于右边值,如果是返回 True 否则返回 False (A > B) 为 False
A<B 检查左边值是否小于右边值,如果是返回 True 否则返回 False (A < B) 为 True
A>=B 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False (A >= B) 为 False
A<=B 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False (A <= B) 为 True
  • 逻辑运算符

下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False

运算符 描述 实例
A&&B 逻辑AND运算符.如果两边的操作数都是True,则条件True,否则为False (A && B) 为 False
A||B 逻辑OR运算符.如果两边的操作数有一个True,则条件True,否则为False (A || B) 为 True
!(A && B) 逻辑NOT运算符.如果条件为True,则逻辑NOT条件False,否则为True !(A && B) 为 True
  • 位运算符

下表列出了所有Go语言的位运算符。假定 A 值为 0101,B 值为 0011,C值为1,D值为8

运算符 描述 实例
A&B 按位与运算,两边都为1结果就为1 0101 & 0011 = 0001
A|B 按位或运算,两边至少有一个为1结果就为1 0101 | 0011 = 0111
A^B 按位亦或运算,两边只有一个为1结果就为1 0101 | 0011 = 0110
^A 按位取反运算 ^0111 = 1000
A&^B 按位清除运算 0101 &^ 0110 = 0001
C<<2 位左移运算 1<<2 = 4
D>>2 位右移运算 8>>2 = 2
  • 赋值运算符

下表列出了所有Go语言的赋值运算符

运算符 描述 实例
= 简单的赋值运算符 C = A + B 将 A + B 表达式结果赋值给 C
+= 相加后再赋值 C += A 等于 C = C + A
-= 相减后再赋值 C -= A 等于 C = C - A
*= 相乘后再赋值 C *= A 等于 C = C * A
/= 相除后再赋值 C /= A 等于 C = C / A
%= 求余后再赋值 C %= A 等于 C = C % A
<<= 左移赋值 C <<= 2 等于 C = C << 2
>>= 右移赋值 C >>= 2 等于 C = C >> 2
&= 位逻辑与赋值 C &= 2 等于 C = C & 2
|= 位逻辑或赋值 C |= 2 等于 C = C |2
^= 位逻辑异或赋值 C ^= 2 等于 C = C ^ 2
  • 其他运算符

下表列出了Go语言的其他运算符

运算符 描述 实例
& 取址运算符 &a 将给出变量a的实际地址
* 指针运算符 *a 将给出指针变量a所指向的值
<- 接收运算符 x,ok := <-ch,表示从channel中接收数据
:= 短变量声明符 var x = 100可以写成 x:= 100
  • 优先级

当一个表达式中包含多个运算符时,就涉及到了运算符的运算优先级,在Go语言中,一元运算符拥有最高优先级,下表列出了Go语言的 二元运算符优先级,从高到低:

优先级| 运算符  
  5  | *   /   %   <<   >>   &   &^
  4  | +   -   |   ^
  3  | ==  !=   <   <=   >   >=
  2  | &&
  1  | ||
优先级 运算符
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
注意:你可以通过使用括号来临时提升某个表达式的整体运算优先级

下面有几点需要注意的:

  • 位操作

除了位操作外,操作数类型必须相同,如果其中一个是无显示类型声明的常量,那么该常量操作数就会 做自动转型,比如:

const v = 20
var a byte = 10
b := a + v  // v常量被自动转型为byte/uint8 类型

const f float32 = 1.2
d := f + v  // v常量被自动转型为float32 类型

位移右操作数必须是无符号数或可以转换的无显示类型常量,比如:

b := 2
x  := 1 << b    //b 是有符号int类型变量,这样是无效的

如果是非常量位移表达式,那么会优先将无显示类型的常量左操作数转型,比如:

a := 1.0 << 3  //常量表达式
c := 1.3 << 3 //constant 1.3 truncated to integer

var s uint = 3
b := 1.0 << s //invalid operation: 1 << s (shift of type float64)
var i int32 = 1.0 << s //自动将1.0 转换为int32
  • 自增/自减

不能用于表达式,只能作为独立语句,比如:

a := 1
++a //syntax error: unexpected ++  不能前置
if (a++) > 1{ //syntax error: unexpected ++ 不能作为表达式
}

a++
p := &a
\*p++  //\*p++相当于(*p)++
  • 指针

不能将指针与内存地址混位一体,内存地址是在内存中每个字节单元的唯一编号,而指针就相当于是用来保存地址的整型变量,自身也会占用内存空间。 比如上例中的p := &a

a :=1 
p := &a
fmt.Println(&a,a)
fmt.Println(&p,p,*p)

输出:
0x1040e0f8 1
0x1040a120 0x1040e0f8 1

并不是所有的对象都能进行取址操作,比如:

m := map[string]string{"hello":"go"}
println(&m["hello"])    //cannot take the address of m["hello"]

指针类型不能做加减法和类型转换,支持相等运算符

a := 10
p := &a
p++   //invalid operation: p++ (non-numeric type *int)
p2 := &a
println(p == p2) //支持相等运算符

零长度对象(声明但未赋值)的对象地址是否相等和具体实现有关,Go编译器会根据其实现的不同,进行不同的优化, 从正确性的角度看,用户是不用关心内存在哪里(堆栈)分配的,不过肯定不为nil,比如

var a,b int //虽然长度为0,但是对象是合法存在的,就拥有合法内存地址,不会为nil
println(&a , &b)
println(&a == &b,&a == nil)
输出:
0x10429f64 0x10429f60
false false

var a,b struct{}
println(&a , &b)
println(&a == &b,&a == nil)
输出:
0x10429f65 0x10429f65
true false

指针没有专门指向成员的”->“运算符, 统一使用”.“,比如:

  a := struct{
    x int
  }{}
  a.x = 10  //相当于a->x
  p := &a
  p.x += 10   //相当于p->x
  println(p.x)

初始化

基本变量的初始化前面已经提到了字面量,在Go语言中,对复合类型变量(数组、切片、字典、结构体) 的初始化是有一些语法限制的,但对基本变量的初始化没有这些限制,比如:

1. 初始化表达式必须含有类型标签
2. 左花括号不能另起一行,必须在类型尾部
3. 多成员初始值必须用逗号","分隔
4. 允许多行编写,但是每行必须以逗号","或右花括号结束

示例如下:

type stu struct{
    name string
    age int
}
 b := stu{
    "zhang",
	  20        //unexpected semicolon or newline, expecting comma or },须以逗号或右花括号结束
	}

b := stu
	{           //syntax error: unexpected }, expecting expression,左花括号不能另起一行
		"zhang",
		20,
	}

var data stu = {"wang",20} //syntax error: missing operand,初始化表达式必须含有类型标签