学习Go语言基础类语法_Day01
1、变量
1.1、变量的声明
1.1.1、语法
var identifier type
1.1.2、批量声明
var (
name string
age int
ok bool
)
1.2、变量的初始化
1.2.1、语法
var 变量名 类型 = 表达式
var name string = "Caidd123"
var site string = "www.Caidd123.com"
var age int = 23
1.2.2、类型推导
var name = "Caidd123"
var age = 23
可以根据初始化值进行类型推导,从而省略类型。
1.2.3、同时初始化多个变量
var name, site, age = "Caidd123", "www.Caidd123.com", 20
1.2.4、*短变量声明(最常用)
name := "Caidd123"
age := 23
注意:只适合在函数内部,函数外面不能使用。
1.2.5、匿名变量(_)
接收到多个变量,有一些变量使用不到时,可以使用下划线_
表示变量名称
name, _ := func()
2、常量
2.1、语法
const constantName [type]= value
2.2、实例
package main
func main() {
const PI float64 = 3.14
const PI2 = 3.1415 // 可以省略类型
const (
width = 100
height = 200
)
const i, j = 1, 2 // 多重赋值
const a, b, c = 1, 2, "foo"
}
const
同时声明多个常量时,如果省略了值则表示和上面一行的值相同
package main
import "fmt"
func main() {
const (
a1 = 100
a2
a3
)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("a2: %v\n", a2)
fmt.Printf("a3: %v\n", a3)
}
运行结果
a1: 100
a2: 100
a3: 100
2.3、iota
iota 比较特殊,可以被认为是一个可被编译器修改的常量,它默认开始值是0
,每调用一次加1
。遇到 const
关键字时被重置为 0
。
2.3.1、iota实例
package main
import "fmt"
func main() {
const (
a1 = iota
a2 = iota
a3 = iota
)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("a2: %v\n", a2)
fmt.Printf("a3: %v\n", a3)
}
运行结果
a1: 0
a2: 1
a3: 2
2.3.2、使用_
跳过某些值
package main
import "fmt"
func main() {
const (
a1 = iota
_
a2 = iota
)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("a2: %v\n", a2)
}
运行结果
a1: 0
a2: 2
2.3.3、iota
声明中间插队
package main
import "fmt"
func main() {
const (
a1 = iota
a2 = 100
a3 = iota
)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("a2: %v\n", a2)
fmt.Printf("a3: %v\n", a3)
}
运行结果
a1: 0
a2: 100
a3: 2
3、基本数据类型
3.1、基本数据类型
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
3.2、数字类型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
3.3、浮点型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
3.4、其他数字类型
以下列出了其他更多的数字类型:
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
3.5、布尔类型
go语言中的布尔类型有两个常量值:true
和false
。布尔类型经常用在条件判断语句,或者循环语句。也可以用在逻辑表达式中。
3.6、字符串
3.6.1、基本定义
字符串字面量使用双引号 ""
或者反引号 '
来创建
双引号 ""
:用来创建可解析的字符串,支持转义,但不能用来引用多行;
反引号 '
:用来创建原生的字符串字面量,可能由多行组成,但不支持转义,并且可以包含除了反引号外其他所有字符
双引号创建可解析的字符串应用最广泛,反引号用来创建原生的字符串则多用于书写多行消息,HTML以及正则表达式。
package main
import "fmt"
func main() {
var str string = "hello Caidd123"
var title string = `
▄████ ▒█████
██▒ ▀█▒▒██▒ ██▒
▒██░▄▄▄░▒██░ ██▒
░▓█ ██▓▒██ ██░
░▒▓███▀▒░ ████▓▒░
░▒ ▒ ░ ▒░▒░▒░
░ ░ ░ ▒ ▒░
░ ░ ░ ░ ░ ░ ▒
░ ░ ░
`
fmt.Printf("str: %v\n", str)
fmt.Printf("title: %v\n", title)
}
运行结果
str: hello Caidd123
title:
▄████ ▒█████
██▒ ▀█▒▒██▒ ██▒
▒██░▄▄▄░▒██░ ██▒
░▓█ ██▓▒██ ██░
░▒▓███▀▒░ ████▓▒░
░▒ ▒ ░ ▒░▒░▒░
░ ░ ░ ▒ ▒░
░ ░ ░ ░ ░ ░ ▒
░ ░ ░
3.6.2、字符串链接
3.6.2.1、字符串支持 +
级联操作和+=
追加操作
package main
import "fmt"
func main() {
name := "Cadd123"
age := "23"
msg := name + " " + age
fmt.Printf("输出结果: %v\n", msg)
fmt.Println("-------------")
msg = ""
msg += name
msg += " "
msg += age
fmt.Printf("输出结果: %v\n", msg)
}
运行结果
输出结果: Cadd123 23
-------------
输出结果: Cadd123 23
golang 里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 gc 带来额外的负担,所以性能比较差
3.6.2.2、fmt.Sprintf()
函数
package main
import "fmt"
func main() {
name := "Caidd123"
age := "23"
msg := fmt.Sprintf("%s,%s", name, age)
fmt.Printf("输出结果: %v\n", msg)
}
运行结果
输出结果: Caidd123,23
内部使用 []byte
实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了 interface
,所以性能也不是很好
3.6.2.3、strings.Join()
package main
import (
"fmt"
"strings"
)
func main() {
name := "Caidd123"
age := "23"
msg := strings.Join([]string{name, age}, ",")
fmt.Printf("输出结果: %v\n", msg)
}
运行结果
输出结果: Caidd123,23
join会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小
3.6.3、转义字符
转义符 | 含义 |
---|---|
\r |
回车符(返回行首) |
\n |
换行符(直接跳到下一行的同列位置) |
\t |
制表符 |
\' |
单引号 |
\" |
双引号 |
\\ |
反斜杠 |
3.6.4、常用方法
方法 | 介绍 |
---|---|
len(str) | 求长度 |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
strings.Join(a[]string, sep string) | join操作 |
3.6.5、byte和rune类型
组成每个字符串的元素叫做“字符”,可以通过遍历或者单个获取字符串元素获得字符。 字符用单引号(’)包裹起来,如:
package main
import "fmt"
func main() {
var a = '华'
var b = 'a'
fmt.Printf("a: %v,%c\n", a, a)
fmt.Printf("b: %v,%c\n", b, b)
}
运行结果
a: 21326,华
b: 97,a
Go 语言的字符有以下两种:
uint8
类型,或者叫 byte 型,代表了ASCII码
的一个字符。rune
类型,代表一个UTF-8字符
。
当需要处理中文、日文或者其他复合字符时,则需要用到rune
类型。rune
类型实际是一个int32
。
Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。
4、运算符
4.1、算术运算符
运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 求余 |
注意: ++
(自增)和--
(自减)在Go语言中是单独的语句,并不是运算符。
4.2、关系运算符
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
4.3、逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。 |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。 |
! | 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。 |
4.4、位运算符
位运算符对整数在内存中的二进制位进行操作。
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。 (两位均为1才为1) |
| | 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1) |
<< | 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。 |
4.5、赋值运算符
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
<<= | 左移后赋值 |
>>= | 右移后赋值 |
&= | 按位与后赋值 |
|= | 按位或后赋值 |
^= | 按位异或后赋值 |
5、流程控制
5.1、if
5.1.1、语法
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
5.1.2、基本实例
5.1.2.1、根据布尔值f判断
import "fmt"
func if1() {
var f = true
if f {
fmt.Println("f is true")
}
fmt.Printf("程序运行结束")
}
func main() {
if1()
}
程序运行结果
f is true
程序运行结束
5.1.2.2、根据年龄判断是否成年
package main
import "fmt"
func if2() {
var age = 23
if age > 18 {
fmt.Println("你是成年人")
}
fmt.Printf("程序运行结束")
}
func main() {
if2()
}
程序运行结果
你是成年人
程序运行结束
5.1.2.3、初始变量可以声明在布尔表达式里面,注意它的作用域
func if3() {
if age := 23; age > 18 {
fmt.Println("你是成年人")
}
fmt.Printf("程序运行结束")
}
func main() {
if3()
}
程序运行结果
你是成年人
程序运行结束
注意:不能使用0或非0表示真假
5.1.2.4、go语言if语句使用提示:
- 不需使用括号将条件包含起来
- 大括号
{}
必须存在,即使只有一行语句 - 左括号必须在
if
或else
的同一行 - 在
if
之后,条件语句之前,可以添加变量初始化语句,使用;
进行分隔
5.1.3、if else
判断一个人是否成年
func else1() {
age := 16
if age >= 18 {
fmt.Println("你是成年人")
} else {
fmt.Println("你还未成年")
}
}
在if前面添加执行语句
func else2() {
if age := 16; age >= 18 {
fmt.Println("你是成年人")
} else {
fmt.Println("你还未成年")
}
}
5.1.4、if else if
func elseif1() {
score := 65
if score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
在if前面添加执行语句
func elseif2() {
if score := 65; score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
5.1.5、if的嵌套
if 布尔表达式 1 {
/* 在布尔表达式 1 为 true 时执行 */
if 布尔表达式 2 {
/* 在布尔表达式 2 为 true 时执行 */
}
}
判断3个数的大小
package main
import "fmt"
// a>b a>c a
// b>a b>c b else c
func f1() {
a := 100
b := 200
c := 3
if a > b {
if a > c {
fmt.Println("a最大")
}
} else {
if b > c {
fmt.Println("b最大")
} else {
fmt.Println("c最大")
}
}
}
func main() {
f1()
}
5.2、for
5.2.1、语法
for 初始语句;条件表达式;结束语句{
循环体语句
}
5.2.2、基本实例1
循环输出1到10
func f() {
for i := 1; i <= 10; i++ {
fmt.Printf("I: %v\n", i)
}
}
运行结果
I: 1
I: 2
I: 3
I: 4
I: 5
I: 6
I: 7
I: 8
I: 9
I: 10
5.2.3、初始条件写在外面
func f() {
i := 1
for ; i <= 10; i++ {
fmt.Printf("I: %v\n", i)
}
}
5.2.4、初始条件和结束条件都可以省略
func f() {
i := 1 // 初始条件
for i <= 10 {
fmt.Printf("i: %v\n", i)
i++ // 结束条件
}
}
5.2.5、无限循环
for {
循环体语句
}
5.2.6、注:
for循环可以通过break
、goto
、return
、panic
语句强制退出循环。
5.3、for range
Go语言中可以使用for range
遍历数组、切片、字符串、map 及通道(channel)。 通过for range
遍历的返回值有以下规律:
- 数组、切片、字符串返回索引和值。
- map返回键和值。
- 通道(channel)只返回通道内的值。
5.3.1、基本实例1:循环数组
func f() {
var a = [5]int{1, 2, 3, 4, 5}
for i, v := range a {
fmt.Printf("i: %d, v: %v\n", i, v)
}
}
func main() {
f()
}
运行结果
i: 0, v: 1
i: 1, v: 2
i: 2, v: 3
i: 3, v: 4
i: 4, v: 5
5.3.2、循环字符串
func f() {
var s = "好好学习,天天向上,up!up!up!"
for i, v := range s {
fmt.Printf("i: %d, v: %c\n", i, v)
}
// %c 按照字符输出
}
func main() {
f()
}
运行结果
i: 0, v: 好
i: 3, v: 好
i: 6, v: 学
i: 9, v: 习
i: 12, v: ,
i: 13, v: 天
i: 16, v: 天
i: 19, v: 向
i: 22, v: 上
i: 25, v: ,
i: 26, v: u
i: 27, v: p
i: 28, v: !
i: 29, v: u
i: 30, v: p
i: 31, v: !
i: 32, v: u
i: 33, v: p
i: 34, v: !
5.3.3、循环切片
func f() {
var s = []int{1, 2, 3, 4, 5}
for i, v := range s {
fmt.Printf("i, %d, v: %v\n", i, v)
}
}
func main() {
f()
}
5.3.4、循环map
func f() {
m := make(map[string]string)
m["name"] = "Caidd123"
m["age"] = "23"
m["email"] = "Caidd123@qq.com"
for k, v := range m {
fmt.Printf("k: %v, v: %v\n", k, v)
}
}
func main() {
f()
}
运行结果
k: age, v: 23
k: email, v: Caidd123@qq.com
k: name, v: Caidd123
5.4、switch
5.4.1、语法
switch var1 {
case val1:
...
case val2:
...
default:
...
}
5.4.2、基本实例1:判断手指
func switchDemo1() {
finger := 3
switch finger {
case 1:
fmt.Println("大拇指")
case 2:
fmt.Println("食指")
case 3:
fmt.Println("中指")
case 4:
fmt.Println("无名指")
case 5:
fmt.Println("小拇指")
default:
fmt.Println("无效的输入!")
}
}
5.4.3、多条件匹配
Go语言规定每个switch
只能有一个分支。
但一个分支可以有多个值,多个case值中间使用英文逗号分隔。
func switchDemo2() {
day := 3
switch day {
case 1, 2, 3, 4, 5:
fmt.Println("工作日")
case 6, 7:
fmt.Println("休息日")
}
}
5.4.4、case可以是条件表达式
func switchDemo3() {
age := 23
switch {
case age < 25:
fmt.Println("好好学习吧")
case age > 25 && age < 35:
fmt.Println("好好工作吧")
case age > 60:
fmt.Println("好好享受吧")
default:
fmt.Println("活着真好")
}
}
5.4.5、fallthrough
fallthrough
可以可以执行满足条件的下一个case
func f3() {
a := 100
switch a {
case 100:
fmt.Println("100")
fallthrough
case 200:
fmt.Println("200")
case 300:
fmt.Println("300")
default:
fmt.Println("other")
}
}
运行结果
100
200
5.4.6、注意事项
- 支持多条件匹配
- 不同的
case
之间不使用break
分隔,默认只会执行一个case
。 - 如果想要执行多个
case
,需要使用fallthrough
关键字,也可用break
终止。 - 分支还可以使用表达式,例如:
a>10
.
5.5、break
break
语句可以结束for
、switch
和select
的代码块。
break
语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的for
、switch
和 select
的代码块上。
跳转到标签处
func f() {
label:
for i := 0; i < 10; i++ {
if i == 5 {
break label
}
fmt.Printf("%v\n", i)
}
fmt.Println("end...")
}
func main() {
f()
}
运行结果
0
1
2
3
4
end...
5.6、continue
continue
只能用在循环中,在go中只能用在for
循环中,它可以终止本次循环,进行下一次循环。
在 continue
语句后添加标签时,表示开始标签对应的循环。
5.6.1、输出1到10之间的偶数
func f() {
for i := 0; i < 10; i++ {
if i%2 == 0 {
fmt.Printf("i: %v\n", i)
}
}
}
func main() {
f()
}
运行结果
i: 0
i: 2
i: 4
i: 6
i: 8
5.6.2、跳转到label
func f() {
for i := 0; i < 5; i++ {
label:
for j := 0; j < 5; j++ {
if i == 2 && j == 2 {
continue label
}
fmt.Printf("i=%d,j=%d\n", i, j)
}
}
}
func main() {
f()
}
运行结果
i=0,j=0
i=0,j=1
i=0,j=2
i=0,j=3
i=0,j=4
i=1,j=0
i=1,j=1
i=1,j=2
i=1,j=3
i=1,j=4
i=2,j=0
i=2,j=1
i=2,j=3
i=2,j=4
i=3,j=0
i=3,j=1
i=3,j=2
i=3,j=3
i=3,j=4
i=4,j=0
i=4,j=1
i=4,j=2
i=4,j=3
i=4,j=4
5.7、goto
goto
语句通过标签进行代码间的无条件跳转。goto
语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto
语句能简化一些代码的实现过程。 例如双层嵌套的for循环要退出时:
5.7.1、跳转到指定标签
func f() {
a := 1
if a == 1 {
goto LABEL1
} else {
fmt.Println("other")
}
LABEL1:
fmt.Printf("next...")
}
func main() {
f()
}
运行结果
next...
5.7.2、跳出双重循环
func f() {
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if i == 2 && j == 2 {
goto LABEL2
}
}
}
LABEL2:
fmt.Println("This is label2")
}
func main() {
f()
}
运行结果
This is label2
6、数组
数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化
6.1、语法:
数组定义的语法如下:
var variable_name [SIZE] variable_type
variable_name
:数组名称
SIZE
:数组长度,必须是常量
variable_type
:数组保存元素的类型
6.2、基本实例1:
package main
import "fmt"
func main() {
var a [3]int // 定义一个int类型的数组a,长度是3
var s [2]string // 定义一个字符串类型的数组s,长度是2
fmt.Printf("a: %T\n", a)
fmt.Printf("s: %T\n", s)
}
运行结果
a: [3]int
s: [2]string
从上面运行结果,我们可以看出,数组和长度和元素类型共同组成了数组的类型。
6.3、初始化
初始化,就是给数组的元素赋值,没有初始化的数组,默认元素值都是零值,布尔类型是false
,字符串是空字符串。
6.3.1、未初始化
package main
import "fmt"
func main() {
var a [3]int // 定义一个int类型的数组a,长度是3
var s [2]string // 定义一个字符串类型的数组s,长度是2
var b [2]bool
fmt.Printf("a: %v\n", a)
fmt.Printf("s: %v\n", s)
fmt.Printf("b: %v\n", b)
}
运行结果
a: [0 0 0]
s: ["" ""]
b: [false false]
6.3.2、使用初始化列表
实例
package main
import "fmt"
func main() {
var a = [3]int{1, 2, 3}
var s = [2]string{"Caidd123", "CC"}
var b = [2]bool{true, false}
a1 := [2]int{1, 2} // 亦可用简短声明来类型推断
fmt.Printf("a: %v\n", a)
fmt.Printf("s: %v\n", s)
fmt.Printf("b: %v\n", b)
fmt.Printf("a1: %v\n", a1)
}
运行结果
a: [1 2 3]
s: [Caidd123 CC]
b: [true false]
a1: [1 2]
6.3.3、可省略数组长度
数组长度可以省略,使用...
代替,更加初始化值得数量自动推断,例如:
package main
import "fmt"
func main() {
var a = [...]int{1, 2, 3}
var s = [...]string{"Caidd123", "CC"}
var b = [...]bool{true, false}
a1 := [...]int{1, 2} // 亦可用简短声明来类型推断
fmt.Printf("a: %v\n", a)
fmt.Printf("s: %v\n", s)
fmt.Printf("b: %v\n", b)
fmt.Printf("a1: %v\n", a1)
}
运行结果
a: [1 2 3]
s: [Caidd123 CC]
b: [true false]
a1: [1 2]
6.3.4、指定位置(索引值)初始化
可以通过指定所有的方式来初始化,未指定所有的默认未零值。
package main
import "fmt"
func main() {
var a = [...]int{0: 1, 2: 2}
var s = [...]string{1: "Cadd123", 2: "CC"}
var b = [...]bool{2: true, 5: false}
a1 := [...]int{1, 2} // 亦可用简短声明来类型推断
fmt.Printf("a: %v\n", a)
fmt.Printf("s: %v\n", s)
fmt.Printf("b: %v\n", b)
fmt.Printf("a1: %v\n", a1)
}
运行结果
a: [1 0 2]
s: [ Cadd123 CC]
b: [false false true false false false]
a1: [1 2]
6.4、访问数组元素
可以通过下标的方式,来访问数组元素。数组的最大下标为数组长度-1,大于这个下标会发生数组越界。
package main
import "fmt"
func main() {
var a [2]int
a[0] = 100
a[1] = 200
fmt.Printf("a[0]: %v\n", a[0])
fmt.Printf("a[0]: %v\n", a[1])
// 修改 a[0] a[1]
a[0] = 1
a[1] = 2
fmt.Println("-----------")
fmt.Printf("a[0]: %v\n", a[0])
fmt.Printf("a[0]: %v\n", a[1])
}
运行结果
a[0]: 100
a[0]: 200
-----------
a[0]: 1
a[0]: 2
6.5、根据数组长度遍历数组
可以根据数组长度,通过for
循环的方式来遍历数组,数组的长度可以使用len
函数获得。
实例
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(a); i++ {
fmt.Printf("a[%d]: %v\n", i, a[i])
}
}
运行结果
a[0]: 1
a[1]: 2
a[2]: 3
a[3]: 4
a[4]: 5
a[5]: 6
6.6、使用for range
遍历数组
还可以使用for range
循环来遍历数组,range返回数组下标和对应的值
实例
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5, 6}
for i, v := range a {
fmt.Printf("a[%d]: %v\n", i, v)
}
}
运行结果
a[0]: 1
a[1]: 2
a[2]: 3
a[3]: 4
a[4]: 5
a[5]: 6
6.7、多维数组
Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)。
6.7.1、二维数组的定义
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
fmt.Println(a[2][1]) //支持索引取值:重庆
}
运行结果
[[北京 上海] [广州 深圳] [成都 重庆]]
重庆
6.7.2、二维数组的遍历
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s\t", v2)
}
fmt.Println()
}
}
运行结果
北京 上海
广州 深圳
成都 重庆
6.7.3、注意
多维数组只有第一层可以使用...
来让编译器推导数组长度。例如:
//支持的写法
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
//不支持多维数组的内层使用...
b := [3][...]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
6.8、数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
7、切片
前面我们学习了数组,数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:我们申请的长度太大浪费内存,太小又不够用。
鉴于上述原因,我们有了go语言的切片,可以把切片理解为,可变长度的数组,其实它底层就是使用数组实现的,增加了自动扩容功能。切片(Slice)是一个拥有相同类型元素的可变长度的序列。
7.1、语法
声明一个切片和声明一个数组类似,只要不添加长度就可以了
var identifier []type
切片是引用类型,可以使用make
函数来创建切片:
//var slice1 []type = make([]type, len)
//也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中capacity为可选参数。
make([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
7.2、基本实例1:
package main
import "fmt"
func main() {
var names []string
var numbers []int
fmt.Printf("names: %v\n", names)
fmt.Printf("numbers: %v\n", numbers)
fmt.Println(names == nil)
fmt.Println(numbers == nil)
}
运行结果
names: []
numbers: []
true
true
7.3、切片的长度和容量
切片拥有自己的长度和容量,我们可以通过使用内置的len()
函数求长度,使用内置的cap()
函数求切片的容量。
实例
package main
import "fmt"
func main() {
var names = []string{"Caidd123", "CC"}
var numbers = []int{1, 2, 3}
fmt.Printf("len: %d cap: %d\n", len(names), cap(names))
fmt.Printf("len: %d cap: %d\n", len(numbers), cap(numbers))
var s1 = make([]string, 2, 3)
fmt.Printf("len: %d cap: %d\n", len(s1), cap(s1))
}
运行结果
len: 2 cap: 2
len: 3 cap: 3
len: 2 cap: 3
7.4、初始化
切片的初始化方法很多,可以直接初始化,也可以使用数组初始化等。
7.4.1、切片如何切分
// 切片 按位置切
func test1() {
var s1 = []int{1, 2, 3, 4, 5, 6}
s2 := s1[0:3] // [)
fmt.Printf("s2: %v\n", s2)
s3 := s1[3:]
fmt.Printf("s3: %v\n", s3)
s4 := s1[2:5]
fmt.Printf("s4: %v\n", s4)
s5 := s1[:]
fmt.Printf("s5: %v\n", s5)
}
运行结果
s2: [1 2 3]
s3: [4 5 6]
s4: [3 4 5]
s5: [1 2 3 4 5 6]
7.4.2、直接初始化
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
fmt.Printf("s: %v\n", s)
}
运行结果
s: [1 2 3]
7.4.3、使用数组初始化
package main
import "fmt"
func main() {
arr := [...]int{1, 2, 3} //定义一个数组;自动推断长度
s1 := arr[:] //切他,从头切到尾
fmt.Printf("s1: %v\n", s1)
}
运行结果
s1: [1 2 3]
7.4.4、切片表达式(使用数组的部分元素初始化)
切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片。 切片表达式中的low和high表示一个索引范围(左包含,右不包含,即左闭右开),得到的切片长度=high-low,容量等于得到的切片的底层数组的容量。
package main
import "fmt"
func main() {
arr := [...]int{1, 2, 3, 4, 5, 6}
s1 := arr[2:5]
fmt.Printf("s1: %v\n", s1)
s2 := arr[2:]
fmt.Printf("s2: %v\n", s2)
s3 := arr[:3]
fmt.Printf("s3: %v\n", s3)
}
运行结果
s1: [3 4 5]
s2: [3 4 5 6]
s3: [1 2 3]
7.4.5、空(nil)切片
一个切片在未初始化之前默认为 nil,长度为 0,容量为0.
package main
import "fmt"
func main() {
var s1 []int
fmt.Println(s1 == nil)
fmt.Printf("len: %d, cap: %d\n", len(s1), cap(s1))
}
运行结果
true
len: 0, cap: 0
7.5、遍历
切片的遍历和数组的遍历非常类似,可以使用for循环索引遍历,或者for range循环。
7.5.1、for循环索引遍历
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
for i := 0; i < len(s1); i++ {
fmt.Printf("s1[%d]: %v\n", i, s1[i])
}
}
运行结果
s1[0]: 1
s1[1]: 2
s1[2]: 3
s1[3]: 4
s1[4]: 5
7.5.2、for range循环
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
for i, v := range s1 {
fmt.Printf("s1[%d]: %v\n", i, v)
}
}
运行结果
s1[0]: 1
s1[1]: 2
s1[2]: 3
s1[3]: 4
s1[4]: 5
7.6、切片元素的添加、删除和copy
切片是一个动态数组,可以使用append()
函数添加元素,go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。由于,切片是引用类型,通过赋值的方式,会修改原有内容,go提供了copy()
函数来拷贝切片
7.6.1、添加元素
package main
import "fmt"
func main() {
s1 := []int{}
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3, 4, 5) // 添加多个元素
fmt.Printf("s1: %v\n", s1)
s3 := []int{3, 4, 5}
s4 := []int{1, 2}
s4 = append(s4, s3...) // 添加另外一个切片
fmt.Printf("s4: %v\n", s4)
}
运行结果
s1: [1 2 3 4 5]
s4: [1 2 3 4 5]
7.6.2、删除元素
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
// 删除索引为2的元素
s1 = append(s1[:2], s1[3:]...)
fmt.Printf("s1: %v\n", s1)
}
运行结果
s1: [1 2 4 5]
公式:要从切片a中删除索引为index
的元素,操作方法是a = append(a[:index], a[index+1:]...)
7.6.3、拷贝切片
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := s1
s1[0] = 100
fmt.Printf("s1: %v\n", s1)
fmt.Printf("s2: %v\n", s2)
fmt.Println("----------")
s3 := make([]int, 3)
copy(s3, s1)
s1[0] = 1
fmt.Printf("s1: %v\n", s1)
fmt.Printf("s3: %v\n", s3)
}
运行结果
s1: [100 2 3]
s2: [100 2 3]
-------------
s1: [1 2 3]
s3: [100 2 3]
从运行结果,我们看到赋值的情况下,原来的变量被修改了,使用copy函数,原来的变量没有被修改。
8、map
map是一种key:value
键值对的数据结构容器。map内部实现是哈希表(hash
)。
map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
map是一种无序的基于key-value
的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
8.1、语法
可以使用内建函数 make 也可以使用 map 关键字来定义 map
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable = make(map[key_data_type]value_data_type)
map_variable
:map变量名称
key_data_type
:key的数据类型
value_data_type
:值得数据类型
8.2、基本实例1:
下面声明一个保存个人信息的map
package main
import "fmt"
func main() {
m1 := make(map[string]string)
m1["name"] = "Caidd123"
m1["age"] = "20"
m1["email"] = "Caidd123@gmail.com"
fmt.Printf("m1: %v\n", m1)
m2 := map[string]string{
"name": "CC",
"age": "20",
"email": "CC@gmail.com",
}
fmt.Printf("m2: %v\n", m2)
}
运行结果
m1: map[age:20 email:Caidd123@gmail.com name:Caidd123]
m2: map[age:20 email:CC@gmail.com name:CC]
8.3、访问map
可以通过下标key获得其值,例如:
package main
import "fmt"
func main() {
m1 := make(map[string]string)
m1["name"] = "Caidd123"
m1["age"] = "20"
m1["email"] = "Caidd123@gmail.com"
name := m1["name"]
age := m1["age"]
email := m1["email"]
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
fmt.Printf("email: %v\n", email)
}
运行结果
name: Caidd123
age: 20
email: Caidd123@gmail.com
8.4、判断某个键是否存在
go语言中有个判断map中键是否存在的特殊写法,格式如下:
value, ok := map[key]
如果ok为true
,存在;否则,不存在。
实例
package main
import "fmt"
func main() {
m1 := make(map[string]string)
m1["name"] = "Caidd123"
m1["age"] = "20"
m1["email"] = "Caidd123@gmail.com"
v, ok := m1["address"]
if ok {
fmt.Println("键存在")
fmt.Println(v)
} else {
fmt.Println("键不存在")
}
}
运行结果
键不存在
8.5、遍历
可以使用for range
循环进行map遍历,得到key和value值。
8.5.1、遍历key
package main
import "fmt"
func main() {
m := make(map[string]string)
m["name"] = "Caidd123"
m["age"] = "20"
m["email"] = "Caidd123@gmail.com"
for key := range m{
fmt.Println(key)
}
}
运行结果
name
age
email
8.5.2、遍历key和value
package main
import "fmt"
func main() {
m := make(map[string]string)
m["name"] = "Caidd123"
m["age"] = "20"
m["email"] = "Caidd123@gmail.com"
for key, value := range m{
fmt.Println(key + ":" +value)
}
}
运行结果
name:Caidd123
age:20
email:Caidd123@gmail.com
9、指针
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号:&
(取地址)和*
(根据地址取值)。
9.1、指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&
字符放在变量前面对变量进行取地址操作。 Go语言中的值类型(int、float、bool、string、array、struct)
都有对应的指针类型,如:*int、*int64、*string
等。
9.2、语法
一个指针变量指向了一个值的内存地址。(也就是我们声明了一个指针之后,可以像变量赋值一样,把一个值的内存地址放入到指针当中。)
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var var_name *var-type
var-type
:为指针类型
var_name
:为指针变量名
*
:用于指定变量是作为一个指针。
9.3、基本实例1:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
9.4、基本实例2:
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
运行结果
a 变量的地址是: c00000a0a8
ip 变量储存的指针地址: c00000a0a8
*ip 变量的值: 20
9.5、指向数组的指针
9.5.1、语法
var ptr [MAX]*int; 表示数组里面的元素的类型是指针类型
9.5.2、指向数组的指针实例:
package main
import "fmt"
const MAX int = 3
func main() {
a := []int{ 1, 3, 5}
var i int
var ptr [MAX]*int;
fmt.Println(ptr) //这个打印出来是[<nil> <nil> <nil>]
for i = 0; i < MAX; i++ {
ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
}
for i = 0; i < MAX; i++ {
fmt.Printf("a[%d] = %d\n", i,*ptr[i] ) //*ptr[i]就是打印出相关指针的值了。
}
}
运行结果
[<nil> <nil> <nil>]
a[0] = 1
a[1] = 3
a[2] = 5
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义