基本语法
基本语法
变量
什么是变量
变量是为存储特定类型的值而提供给内存位置的名称。变量的本质就是一小块内存,用于存储数据,在程序运行过程中数值可以改变。
变量名以字母或下划线开头,由一个或多个字母、数字、下划线组成。
声明变量的三种方式
第一种:指定变量类型,声明后若不赋值,使用默认值
var name type
name = value第二种:根据值自行判定变量类型(类型推断 Type inference)
如果一个变量有一个初始值,Go 将自动能够使用初始值来推断该变量的类型。因此,如果变量具有初始值,则可以省略变量声明中的类型。
var name = value第三种:简短声明,省略 var
name := value注意:
:=左侧的变量不应该是已经声明过的(多个变量同时声明时,至少保证一个是新变量),否则会导致编译错误。这种方式只能被用在函数体内,不可以用于全局变量的声明与赋值。
示例代码:
package main
var a = "Hello"
var b string = "World"
var c bool
func main() {
println(a, b, c)
}运行结果:
Hello World false多变量声明
第一种:以逗号分隔,声明与赋值分开,若不赋值,存在默认值
var name1, name2, name3 type
name1, name2, name3 = v1, v2, v3第二种:直接赋值,变量类型可以是不同的类型
var name1, name2, name3 = v1, v2, v3第三种:集合类型(批量声明)
var (
name1 type1
name2 type2
)变量的注意事项
- 变量必须先定义才能使用
- Go 语言是静态语言,要求变量的类型和赋值的类型必须一致
- 变量名不能冲突(同一个作用域内不能冲突)
- 简短定义方式
:=,左边的变量名至少有一个是新的 - 简短定义方式,不能定义全局变量
- 变量的零值(默认值):数值类型为
0,布尔类型为false,字符串为"",引用类型为nil - 变量定义了就要使用,否则无法通过编译
在同一作用域中,已存在同名的变量,则之后的声明初始化退化为赋值操作。但这个前提是,最少要有一个新的变量被定义,且在同一作用域:
package main
import "fmt"
func main() {
x := 140
fmt.Println(&x)
x, y := 200, "abc" // y 是新变量,x 退化为赋值
fmt.Println(&x, x) // &x 地址相同,说明是同一个变量
fmt.Print(y)
}运行结果:
0xc04200a2b0
0xc04200a2b0 200
abc如果声明了一个局部变量却没有在相同的代码块中使用它,会得到编译错误 a declared and not used。单纯地给 a 赋值也是不够的,这个值必须被使用。
常量
常量声明
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
const identifier [type] = value显式类型定义:const b string = "abc"
隐式类型定义:const b = "abc"示例:
package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" // 多重赋值
area = LENGTH * WIDTH
fmt.Printf("面积为 : %d", area)
println()
println(a, b, c)
}运行结果:
面积为 : 50
1 false str常量可以作为枚举,常量组:
const (
Unknown = 0
Female = 1
Male = 2
)常量组中如不指定类型和初始化值,则与上一行非空常量右值相同:
package main
import "fmt"
func main() {
const (
x uint16 = 16
y // 与上一行相同:uint16, 16
s = "abc"
z // 与上一行相同:string, "abc"
)
fmt.Printf("%T,%v\n", y, y) // uint16,16
fmt.Printf("%T,%v\n", z, z) // string,abc
}常量的注意事项:
- 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型
- 不曾使用的常量,在编译时不会报错(这一点与变量不同)
- 显式指定类型的时候,必须确保常量左右值类型一致,需要时可做显式类型转换
- 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址
iota
iota 是特殊常量,可以认为是一个可以被编译器修改的常量,常用作枚举值。
iota 在 const 关键字出现时被重置为 0,const 中每新增一行常量声明将使 iota 计数一次。
const (
a = iota // 0
b // 1
c // 2
)iota 用法示例:
package main
import "fmt"
func main() {
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 独立值,iota += 1(iota 变成 3)
e // "ha",iota += 1(iota 变成 4)
f = 100 // iota += 1(iota 变成 5)
g // 100,iota += 1(iota 变成 6)
h = iota // 7,恢复计数
i // 8
)
fmt.Println(a, b, c, d, e, f, g, h, i)
}运行结果:
0 1 2 ha ha 100 100 7 8如果中断 iota 自增,则必须显式恢复。且后续自增值按行序递增。自增默认是 int 类型,可以自行进行显式指定类型。
基本数据类型
Go 中可用的基本数据类型分为以下几类:
| 分类 | 类型 | 说明 |
|---|---|---|
| 布尔型 | bool | 值只能是 true 或 false |
| 整数型 | int8, int16, int32, int64 | 有符号整数 |
| 整数型 | uint8, uint16, uint32, uint64 | 无符号整数 |
| 整数型 | int, uint | 根据底层平台,32 或 64 位 |
| 整数型 | uintptr | 无符号整型,用于存放一个指针 |
| 整数型 | byte | 类似 uint8 |
| 整数型 | rune | 类似 int32,表示 Unicode 码点 |
| 浮点型 | float32, float64 | IEEE-754 浮点型 |
| 复数型 | complex64, complex128 | 32/64 位实数和虚数 |
| 字符串型 | string | UTF-8 编码的字符序列 |
布尔型 bool
布尔型的值只可以是常量 true 或者 false:
var b bool = true数值型
整数型:
| 类型 | 范围 |
|---|---|
int8 | -128 到 127 |
int16 | -32768 到 32767 |
int32 | -2147483648 到 2147483647 |
int64 | -9223372036854775808 到 9223372036854775807 |
uint8 | 0 到 255 |
uint16 | 0 到 65535 |
uint32 | 0 到 4294967295 |
uint64 | 0 到 18446744073709551615 |
int和uint:根据底层平台决定大小,32 位系统为 32 位,64 位系统为 64 位。除非需要使用特定大小的整数,否则通常应该使用int来表示整数。
浮点型:
| 类型 | 说明 |
|---|---|
float32 | IEEE-754 32 位浮点型数 |
float64 | IEEE-754 64 位浮点型数 |
complex64 | 32 位实数和虚数 |
complex128 | 64 位实数和虚数 |
字符串型
字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的,使用 UTF-8 编码标识 Unicode 文本。
var str string
str = "Hello World"类型转换
Go 中类型转换的语法格式:Type(Value)
- 常量:在有需要的时候,会自动转型
- 变量:需要手动转型
T(V)
注意:只有兼容类型之间可以转换。
复合类型(派生类型)
以下是 Go 中的复合类型(将在后续章节详细介绍):
| 类型 | 说明 |
|---|---|
| 指针类型(Pointer) | *T |
| 数组类型 | [N]T |
| 切片类型 | []T |
| 结构化类型(struct) | 自定义结构体 |
| Channel 类型 | chan T |
| 函数类型 | func(T) U |
| 接口类型(interface) | 抽象接口 |
| Map 类型 | map[K]V |
运算符
表达式 (a + b) * c 中,a、b、c 叫做操作数,+、* 叫做运算符。
算术运算符
+ // 加
- // 减
* // 乘
/ // 除
% // 求余
++ // 自增
-- // 自减关系运算符
== // 等于
!= // 不等于
> // 大于
< // 小于
>= // 大于等于
<= // 小于等于逻辑运算符
| 运算符 | 描述 |
|---|---|
&& | 逻辑与。如果两个操作数都非零,则条件为真 |
|| | 逻辑或。如果任意一个操作数非零,则条件为真 |
! | 逻辑非。反转操作数的逻辑状态 |
位运算符
位运算符对整数在内存中的二进制位进行操作。
| 运算符 | 描述 | 示例(A=60, B=13) |
|---|---|---|
& | 按位与,两个操作数对应位都为 1 时结果为 1 | A & B = 12(0000 1100) |
| | 按位或,两个操作数对应位有一个为 1 时结果为 1 | A | B = 61(0011 1101) |
^ | 按位异或,两个操作数对应位不同时结果为 1 | A ^ B = 49(0011 0001) |
&^ | 按位清空,将左操作数中对应的右操作数为 1 的位清零 | A &^ B = 48(0011 0000) |
<< | 左移,左操作数按位左移右操作数指定的位数 | A << 2 = 240(1111 0000) |
>> | 右移,左操作数按位右移右操作数指定的位数 | A >> 2 = 15(0000 1111) |
真值表(按位与/或/异或):
| A | B | A&B | A|B | A^B |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
| 1 | 0 | 0 | 1 | 1 |
赋值运算符
| 运算符 | 描述 | 等价于 |
|---|---|---|
= | 简单赋值 | C = A + B |
+= | 相加并赋值 | 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 |
运算符优先级
优先级由高到低排列,二元运算符的运算方向均是从左至右:
| 优先级 | 运算符 |
|---|---|
| 7(最高) | ~ ! ++ -- |
| 6 | * / % << >> & &^ |
| 5 | + - ^ |
| 4 | == != < <= >= > |
| 3 | <-(channel 操作符) |
| 2 | && |
| 1(最低) | || |
可以通过使用括号来临时提升某个表达式的整体运算优先级。