一、数据类型
- 布尔型:布尔型的值只可以是常量 true 或者 false。eg:var bo bool = true。布尔型无法参与数值运算,也无法与其他类型进行转换
- 数字类型:整型 int 、浮点型 float32、float64。
- 字符串类型:字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
派生类型:
- 指针类型(Pointer)
- 数组类型
- 结构化类型(struct)
- Channel 类型
- 函数类型
- 切片类型
- 接口类型(interface)
- Map 类型
a、数字类型
-
整数
-
无符号整数类型:
-
uint8:无符号 8 位整型 (0 到 255)
- uint16:无符号 16 位整型 (0 到 65535)
- uint32:无符号 32 位整型 (0 到 4294967295)
- uint64:无符号 64 位整型 (0 到 18446744073709551615)
-
- 有符号整数类型:
- int8:有符号 8 位整型 (-128 到 127)
- int16:有符号 16 位整型 (-32768 到 32767)
- int32:有符号 32 位整型 (-2147483648 到 2147483647)
- int64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
-
注:
int 表示有符号整数,uint 表示无符号整数;int 和 uint 所能表示的整数大小根据计算机硬件和编译器不同,会在 32bit 或 64bit 之间变化。
Unicode 字符的 rune 类型和 int32 类型是等价的,byte 和 uint8 也是等价类型。
另外还有 无符号的整数类型 uintptr,它没有指定具体的 bit 大小但足以容纳指针。uintptr 类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
-
浮点数
- float32:常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38;最小值为 1.4e-45 ;可提供大约 6 个十进制数的精度。
- float64:常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308;最小值为 4.9e-324 ;可提供大约 15 个十进制数的精度,应优先使用 float64 类型
-
复数
- complex64(32 位实数和虚数)
- complex128(64 位实数和虚数)
- complex64(32 位实数和虚数)
复数使用 re+imi 来表示,其中 re 代表实数部分,im 代表虚数部分,i 代表根号负 1;eg:-1+0i
内建的 real 和 imag 函数分别返回复数的实部和虚部
b、字符串类型
- 转义字符:
- :换行符
- :回车符
- :tab 键
- u 或 U:Unicode 字符
- \:反斜杠自身
- 索引访问:
- 字符串 str 的第 1 个字节:str[0]
- 第 i 个字节:str[i - 1]
- 最后 1 个字节:str[len(str)-1]
注意:获取字符串中某个字节的地址属于非法行为,例如 &str[i]。
- 字符串拼接符:”+“ 直接连接字符串
- 定义多行字符串: “ `` ”
- len(): 表示字符串的 ASCII 字符个数或字节长度 ,返回一个 int 整型
- 字符串遍历:
- ASCII 字符串遍历直接使用下标。
- Unicode 字符串遍历用 for range。
- 字符串截取使用类似python的切片存在,利用索引截取:s = str[start:end]
- 格式化操作符:
- %v 按值的本来值输出
- %+v 在 %v 基础上,对结构体字段名和值进行展开
- %#v 输出 Go 语言语法格式的值
- %T 输出 Go 语言语法格式的类型和值
- %% 输出 % 本体
- %b 整型以二进制方式显示
- %o 整型以八进制方式显示
-
%d 整型以十进制方式显示
-
%x 整型以十六进制方式显示
-
%X 整型以十六进制、字母大写方式显示
-
%U Unicode 字符
-
%f 浮点数
-
%p 指针,十六进制方式显示
c、字符类型
- uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。byte 类型是 uint8 的别名
- rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。
- Unicode包的内置函数:
- 判断是否为字母:unicode.IsLetter(ch)
- 判断是否为数字:unicode.IsDigit(ch)
- 判断是否为空白符号:unicode.IsSpace(ch)
二、变量 & 常量
变量
// 第一种声明变量方式: 指定变量类型,进行初始化或使用默认值 var boolean1 bool // 声明单个变量 var num1, num2 int // 声明多个相同类型的变量 // 第二种声明变量方式: var num3 int = 5 var num3 = 5 // 系统根据 值 自行判定变量类型 // 第三种声明变量方式: 省略 var,若 := 左侧未声明新的变量,则产生编译错误 str1 := "u_u" str1, str2, str3 := "-_-", "^_^", "8_8"
注:
- 变量命名遵循小驼峰命名法
- := 只能用于函数内部定义变量,且使用 := 时必须确保此变量在此之前从未声明使用过,var 一般用于定义全局变量
- _ 是一个特殊的变量名,任何赋予它的值都会被丢弃
- 声明的局部变量必须在其代码块中使用,全局变量可不必
- reflect.TypeOf(variable):可获取变量 variable 的类型
常量
// 定义语法: const name [type] = value const pi = 3.14159 // const 定义常量时,必须要有初始值 const Pi float32 = 3.1415926 const ( e = 2.7182818 pi = 3.1415926 ) // 常量使用关键字 const 定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,并且只能是布尔型、数字型(整数型、浮点型和复数)和字符串型。 // 常量的值必须是能够在编译时就能够确定的,可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。
iota 枚举
// iota关键字通常用来声明enum的时候用,它默认开始值是0,每调用一次+1. const ( first = iota second third fourth = "power" fifth sixth = 22 seventh eighth = iota ninth tenth ) // [Output]: 0 1 2 power power 22 22 7 8 9
三、运算符
算术运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 求余 |
++ | 自增 |
-- | 自减 |
关系运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
逻辑运算符 | 描述 |
---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 |
赋值运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
<<= | 左移后赋值 |
>>= | 右移后赋值 |
&= | 按位与后赋值 |
^= | 按位异或后赋值 |
|= | 按位或后赋值 |
其他运算符 | 描述 |
---|---|
& | 返回变量存储地址 |
* | 指针变量。 |
关键字
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
标识符
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
四、分支循环语句
分支语句
// if 语句 if condition { // condition 为 布尔类型 // do something } // if-else 语句 if condition { // if 为真即成立,执行 if代码块;否则执行 else代码块 // do something } else { // do something } // if-else if-else 语句 if condition1 { // do something } else if condition2 { // do something else } else { // catch-all or default } // if 的特殊姿势 if err := Connect(); err != nil { // Connect() 为带有返回的函数,此条件先获取函数的返回值,再根据这个返回值进行判断 fmt.Println(err) return } // switch 语句 var a = "hello" switch a { case "hello": fmt.Println(1) case "world": fmt.Println(2) default: fmt.Println(0) } // 一分支多值 var a = "mum" switch a { case "mum", "daddy": fmt.Println("family") } // 分支表达式 var r int = 11 switch { case r > 10 && r < 20: fmt.Println(r) } 注意: 每个 switch 只能有一个 default 分支 switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。 switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。 switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。 // select 语句 select { case communication clause : statement(s); case communication clause : statement(s); // 可以定义任意数量的 case default : // 可选 statement(s); } // select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。 每个 case 都必须是一个通信 所有 channel 表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通信可以进行,它就执行,其他被忽略。 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。 否则: 如果有 default 子句,则执行该语句。 如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。
Go 没有三目运算符,所以不支持 ?: 形式的条件判断
循环语句
// for 循环 for index, elem := range nums { // do something } for { // 死循环 // do something } // break & continue 语句 for _, elem := range nums { switch elem{ case "": contiue case "bye", "quit": break default: fmt.Println("Hello World") } } // goto 语句: 无条件地转移到代码中 goto语句 指定的标签中 执行标签内代码 func main() { for x := 0; x < 10; x++ { for y := 0; y < 10; y++ { if y == 2 { // 跳转到标签 goto breakHere } } } // 手动返回, 避免执行进入标签 return // 标签 breakHere: fmt.Println("done") }
五、数组
- go 的数组内所有元素都是同一类型、是固定长度的有序集合
- 声明语法: var aryName [arySize]elemType
- 相同类型的两个数组支持 “!=” 和 “==” 比较,但不能比较两个数组的大小
- 数组的指针:*[3]int; 指针数组:[2]*int
// 声明数组 var ary[2] int // 数组赋值 ary[0] = 11 // 初始化数组 var balanceSize = [5] float32{100, 2.0, 3.1, 5.5, 6.2}
var balanceSize = [...]float32{100, 2.0, 3.1, 5.5, 6.2} // 可省略长度,`...`的方式,会自动根据元素个数来计算长度
// 声明多维数组并赋值 var secondly [2][3] int for k:=0; k<len(secondly); k++ { for m:=0; m<len(secondly[k]); m++ { secondly[k][m] = k + m + 100 } } // 初始化多维数组 var secondlyArray = [2][3] int{ {1, 2, 3}, {4, 5, 6}, } // 索引访问数组元素 fmt.Println(balanceSize[0]) // 索引设置数组元素值 balanceSize[0] = 22.2 // 返回数组长度 fmt.Println(len(balanceSize))
六、切片
- 切片的长度是可变的,声明时无需指定切片长度,可以使用数组的方式设置、访问元素、len 获取切片的长度
- 额外的操作:
- append() 追加元素,返回一个拥有新元素的新切片,append不会改变原切片,而是生成了一个新切片。
- 取切片操作:slice[start:end];包含 start,而不包含end
- 也可创建多维切片,拥有以上所有操作
- 数组和切片的定义方式的区别在于
[]
之中是否有固定长度
或者推断长度标志符...
// 声明切片: var sliceName []sliceType var sliceTest [] int // 第一种方式 sliceTest := make([]string, 3) // 创建一个长度为3,存储字符串的切片 // 追加元素 sliceTest = append(sliceTest, "power") sliceTest = append(sliceTest, "top", "one") // 取切片 newSlice := sliceTest[:2] // 取前两个元素 newSlice := sliceTest[:] // 取所有元素 newSlice := sliceTest[1:5] // 取索引为1的元素到索引为4的元素 // 同时声明和初始化一个切片 t := []string{"g", "h", "i"} // 创建二维切片 twoD := make([][]int, 3) for i := 0; i < 3; i++ { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := 0; j < innerLen; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) } // copy var nilSlice []int nilSlice = append(nilSlice, 3, 5, 21, 31, 18) copySlice := make([]int, len(nilSlice), (cap(nilSlice))*2) copy(copySlice, nilSlice)
七、字典 map
// 声明 map :var variable map[keyType]valueType var mapVariable map[string]string // 默认为 map[] nil // make函数 定义map mapVariable := make(map[string]string) // make 定义并初始化 test := map[string]float32{"a": 1, "b": 2, "c": 3} // map 是一种引用,两个map指向同一底层,一个修改,另一个也变化 otherTest := test otherTest["c"] = 6 fmt.Println("modify otherTest: ", otherTest) fmt.Println("modify test: ", test) // [Output]:modify otherTest: map[a:1 b:2 c:6] // [Output]:modify test: map[a:1 b:2 c:6] // 添加键值 mapVariable["s"] = "u5bb6u8431" mapVariable["h"] = "u99a5u7504" mapVariable["e"] = "u5609u6866" // 遍历取出键值 for key, value := range mapVariable{ fmt.Println(key, value) } // 判断是否存在某键 name, ok := mapVariable["s"] // name:若key存在返回所对应的value,ok:此key是否存在(bool型) // 删除键值 delete(map_variable1, "she") // 若key不存在,则忽略
// key 对应 多个 value:切片
mp1 := make(map[int][]int)
mp2 := make(map[int]*[]int)
八、make & new
make
- make用于内建类型(map、slice、channel)的内存分配
- make(T,args)与new(T)有着不同的功能,make只能创建slice,map,channel,并且返回一个有初始值(非零)的T类型,而不是*T
new
- new用于各种类型的内存分配【new返回指针】
- new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值(GO语言的术语:返回了一个指针,指向新分配的类型T的零值)
ending
- 使用 new 创建的某个类型的对象,返回的是 内存地址;使用 make 创建的是三种类型之一的对象,返回的是带有初始值的value
- new 负责分配内存,new(T) 返回 *T:代表的是 T类型的指针,是一个指向零值的 T 类型指针
- make 负责初始化值,make(T) 返回初始化后的 T ,而非指针,make 仅适用于slice,map 和channel
// make & new 的区别 // make slices := make([]int, 2) fmt.Println("slices: ", slices) maps := make(map[int]int) fmt.Println("maps: ", maps) // 【Output】:slices: [0 0] // 【Output】:maps: map[] // 声明结构体 type Info struct { } // new num := new(int) fmt.Println("num: ", num) structVariable := new(Info) fmt.Println("structVariable", structVariable) //【Output】:num: 0xc00000a120 //【Output】:structVariable &{} // make & new 区别的实例 type Foo struct { // 声明结构体 weight float64 age int } // 声明 var foo1 Foo fmt.Println("foo1: ", foo1) foo1.age = 18 fmt.Println("foo1: ", foo1) fmt.Println() // 定义 foo2 := Foo{} fmt.Println("foo2: ", foo2) foo2.age = 19 fmt.Println("foo2: ", foo2) fmt.Println() // make 只能创建 channel、slice、map // & 取址 foo3 := &Foo{} fmt.Println("foo3: ", foo3) fmt.Println("foo3: ", *foo3) foo3.age = 20 fmt.Println("foo3: ", foo3) fmt.Println() // new foo4 := new(Foo) fmt.Println("foo4: ", foo4) fmt.Println("foo4: ", *foo4) foo4.age = 21 fmt.Println("foo4: ", foo4) fmt.Println() var foo5 *Foo = &Foo{} fmt.Println("foo5: ", foo5) fmt.Println("foo5: ", *foo5) foo5.age = 22 fmt.Println("foo5: ", foo5) // 以下是输出结果 foo1: {0 0} foo1: {0 18} foo2: {0 0} foo2: {0 19} foo3: &{0 0} foo3: {0 0} foo3: &{0 20} foo4: &{0 0} foo4: &{0 21} foo5: &{0 0} foo5: &{0 22} 注:foo1 & foo2 为相同的类型,输出的都是 结构体Foo类型的值 ==> {0 0} 由于没有给字段赋值,输出的都是初始值 foo3 & foo4 & foo5 为相同的类型,输出的都是 结构体Foo类型的内存地址(指针) ==> &{0 0} 只有在前面加了 * 之后才是取到了他的值:*foo3、*foo4、*foo5