目录
运算符
运算符用于在程序运行时执行数学或逻辑运算。
Go 语言内置的运算符有:
-
算术运算符
-
关系运算符
-
逻辑运算符
-
位运算符
-
赋值运算符
算术运算符
运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 求余 |
注意:
++
(自增)和--
(自减)在Go语言中是单独的语句,并不是运算符。
package main
import "fmt"
func main() {
a := 10
b := 20
fmt.Println(a + b)
fmt.Println(a - b)
fmt.Println(a * b)
fmt.Println(a / b)
fmt.Println(a % b)
}
30
-10
200
0
10
关系运算符
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
package main
import "fmt"
func main() {
a := 10
b := 20
fmt.Println(a == b) // false
fmt.Println(a <= b) // true
fmt.Println(a >= b) // false
fmt.Println(a != b) // true
fmt.Println(a > b) // false
fmt.Println(a < b) // true
}
false
true
false
true
false
true
逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑 and 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。 |
|| | 逻辑 or 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。 |
! | 逻辑 not 运算符。 如果条件为 True,则为 False,否则为 True。 |
package main
import "fmt"
func main() {
a := 10
b := 20
fmt.Println(a < b && a == b) // false
fmt.Println(a < b || a == b) // true
fmt.Println(!(a > b)) // true
}
false
true
true
位运算符
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。 (两位均为1才为1) |
| | 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1) |
<< | 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。 |
package main
import "fmt"
func main() {
a := 2 // 010
b := 3 // 011
fmt.Println(a & b) // 010 --> 2(上下对应位置都是1就是1)
fmt.Println(a | b) // 011 --> 3(上下对应位置只要有一个1就是1)
fmt.Println(a ^ b) // 001 --> 1(上下对应位置只要不相等就是1)
fmt.Println(a << b) // a*2^b --> 2*2^3 --> 16
fmt.Println(a >> b) // a/2^b --> 2/2^3 --> 0.25
}
2
3
1
16
0
赋值运算符
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
<<= | 左移后赋值 |
>>= | 右移后赋值 |
&= | 按位与后赋值 |
|= | 按位或后赋值 |
^= | 按位异或后赋值 |
package main
import "fmt"
func main() {
var a1 int
a1 = 5 // 赋值运算符
fmt.Println(a1)
a1++ // a1+1
fmt.Println(a1)
a1-- // a1-1
fmt.Println(a1)
a1 += 5 // 相加之后再赋值
fmt.Println(a1)
a1 -= 3 // 相减之后再赋值
fmt.Println(a1)
a1 *= 2 // 相乘后再赋值
fmt.Println(a1)
a1 /= 3 // 相除后再赋值
fmt.Println(a1)
a1 %= 3 // 求余后再赋值
fmt.Println(a1)
}
5
6
5
10
7
14
4
1
流程控制
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,Go语言中最常用的流程控制有if
和for
,而switch
和goto
主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。
if判断
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
package main
import "fmt"
func main() {
age := 19
if age > 18 {
fmt.Println("你已经成年了!")
} else {
fmt.Println("你还没有成年!")
}
if age > 35 {
fmt.Println("人到中年了")
} else if age > 18 {
fmt.Println("年轻就是好")
} else {
fmt.Println("好好学习,天天上当")
}
}
你已经成年了!
年轻就是好
Go语言规定与if
匹配的左括号{
必须与if和表达式
放在同一行,{
放在其他位置会触发编译错误。 同理,与else
匹配的{
也必须与else
写在同一行,else
也必须与上一个if
或else if
右边的大括号在同一行。
if判断的特殊写法
if条件判断还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,举个例子:
func ifDemo2() {
if score := 65; score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
for循环
Go 语言中的所有循环类型均可以使用for
关键字来完成。
for循环的基本格式如下:
for 初始语句;条件表达式;结束语句{
循环体语句
}
条件表达式返回true
时循环体不停地进行循环,直到条件表达式返回false
时自动退出循环。
package main
import "fmt"
func main() {
for i := 0; i <= 10; i++ {
fmt.Println(i)
}
}
0
1
2
3
4
5
6
7
8
9
10
for循环的初始语句可以被忽略,但是初始语句后的分号必须要写,例如:
package main
import "fmt"
func main() {
num1 := 5
for ; num1 <= 10; num1++ {
fmt.Println(num1)
}
}
5
6
7
8
9
for循环的初始语句和结束语句都可以省略,例如:
package main
import "fmt"
func main() {
/*num1 := 5
for ; num1 < 10; num1++ {
fmt.Println(num1)
} */
num1 := 5
for num1 < 10 {
fmt.Println(num1)
num1++
}
}
5
6
7
8
9
这种写法类似于其他编程语言中的while
,在while
后添加一个条件表达式,满足条件表达式时持续循环,否则结束循环。
死循环
注意:Go语言性能比较优异,切记不可操作此循环(了解即可),否则可能导致电脑卡死或蓝屏。
for {
循环体语句
}
for循环可以通过break
、goto
、return
、panic
语句强制退出循环。
for range
循环
Go语言中可以使用for range
遍历数组、切片、字符串、map 及通道(channel)。 通过for range
遍历的返回值有以下规律:
-
数组、切片、字符串返回索引和值。
-
map返回键和值。
-
通道(channel)只返回通道内的值。
package main
import "fmt"
func main() {
name := "Hello Go语言"
for index, value := range name {
fmt.Printf("%d-%c\n", index, value)
}
}
0-H
1-e
2-l
3-l
4-o
5-
6-G
7-o
8-语
11-言
练习
- 九九乘法表
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%d\t", j, i, i*j)
}
fmt.Printf("\n")
}
}
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
break和continue
break为结束循环,continue为跳过本次循环继续执行下一次循环。
package main
import "fmt"
func main() {
// 当i=5时结束当前循环
/* for i := 0; i <= 10; i++ {
if i == 5 {
break
} else {
fmt.Println(i)
}
}
fmt.Println("Over!!!") */
// 当i=5时跳过本次循环
for i := 0; i <= 10; i++ {
if i == 5 {
continue
} else {
fmt.Println(i)
}
}
fmt.Println("Over!!!")
}
0
1
2
3
4
6
7
8
9
10
Over!!!
switch case
使用switch
语句可方便地对大量的值进行条件判断。
package main
import "fmt"
func main() {
num1 := 3
switch num1 {
case 1:
fmt.Println("张三")
case 2:
fmt.Println("李四")
case 3:
fmt.Println("王五")
case 4:
fmt.Println("赵六")
case 5:
fmt.Println("小七")
default:
fmt.Println("无效输入!!!")
}
}
王五
一个分支可以有多个值,多个case值中间使用英文逗号分隔。
package main
import "fmt"
func main() {
switch num2 := 3; num2 {
case 1, 3, 5, 7, 9:
fmt.Println("这是奇数")
case 2, 4, 6, 8, 10:
fmt.Println("这是偶数")
default:
fmt.Println(num2)
}
}
这是奇数
分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量。例如:
package main
import "fmt"
func main() {
age := -3
switch {
case age <= 18 && age > 0:
fmt.Println("好好学习吧")
case age <= 35 && age > 18:
fmt.Println("好好赚钱奥")
case age > 35:
fmt.Println("还是身体重要!!!")
default:
fmt.Println("无效的输入")
}
}
无效的输入
goto(跳转到指定的标签)
goto
语句通过标签进行代码间的无条件跳转。goto
语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto
语句能简化一些代码的实现过程。 例如双层嵌套的for循环要退出时:
package main
import "fmt"
func main() {
var breakTag bool // 初始化标志
for i := 0; i < 10; i++ {
for j := 0; j <= i; j++ {
if j == 5 {
breakTag = true
} else {
fmt.Println(i, j)
}
}
if breakTag {
break
}
}
}
使用goto
语句能简化代码:
package main
import "fmt"
func main() {
for i := 0; i <= 10; i++ {
for j := 1; j <= i; j++ {
if j == 5 {
goto breakTag // 设置结束标志
}
fmt.Print(j)
}
fmt.Print("\n")
}
// 结束
breakTag:
fmt.Println("循环已经结束")
}
1
12
123
1234
1234循环已经结束
(Array)数组
数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。基本语法如下:
package main
import "fmt"
func main() {
var arr1 [3]bool
fmt.Printf("%T\n", arr1)
fmt.Println(arr1)
}
[3]bool
[false false false]
注意:Go语言中数组的长度就是数组类型的一部分!!!
数组定义
var 数组的名字 [数组的数量]数组的类型
var arr [4]int
比如:var arr1 [5]int
, 数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。 [5]int
和[10]int
是不同的类型。
数组可以通过下标进行访问,下标是从0
开始,最后一个元素下标是:len-1
,访问越界(下标在合法范围之外),则触发访问越界,会报错。
package main
import "fmt"
func main() {
var arr1 [3]bool
fmt.Println(arr1[5])
}
# command-line-arguments
day3\数组.go:7:19: invalid argument: array index 5 out of bounds [0:3]
数组的初始化
如果不初始化,默认的元素都是零值(布尔值:false;整型和浮点型:0;字符串:"")。
方式1:
package main
import "fmt"
var name = [3]string{"老王", "胖子", "大胖子"}
func main() {
fmt.Println(name)
arr1 := [3]bool{true, true, false}
fmt.Println(arr1)
}
[老王 胖子 大胖子]
[true true false]
方式2:
根据初始值的个数自行推断数组的长度,例如:
package main
import "fmt"
func main() {
// 方式1
/* arr1 := [3]bool{true, true, false}
fmt.Println(arr1) */
// 方式2
arr2 := [...]string{"hpl", "xxx", "aaa", "laowang"}
fmt.Println(arr2)
}
[hpl xxx aaa laowang]
方式3:
使用指定索引值的方式来初始化数组,例如:
package main
import "fmt"
func main() {
// 方式1
/* arr1 := [3]bool{true, true, false}
fmt.Println(arr1) */
// 方式2
/* arr2 := [...]string{"hpl", "xxx", "aaa", "laowang"}
fmt.Println(arr2) */
// 方式3
arr3 := [5]int{1: 2, 4: 666}
fmt.Println(arr3)
}
[0 2 0 0 666]
数组的遍历
数组的遍历有两种方式:
package main
import "fmt"
func main() {
address := [...]string{"西安", "北京", "汉中", "洛阳"}
// 方式1
for i := 0; i < len(address); i++ {
fmt.Println(address[i])
}
fmt.Println("---------------------")
// 方式2
for index, value := range address {
fmt.Println(index, value)
}
}
西安
北京
汉中
洛阳
---------------------
0 西安
1 北京
2 汉中
3 洛阳
多维数组
Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)。
package main
import "fmt"
func main() {
// 多维数组
arr1 := [...][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
fmt.Println(arr1)
}
[[1 2 3] [4 5 6] [7 8 9]]
多维数组的遍历
package main
import "fmt"
func main() {
// 多维数组
arr1 := [...][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
// fmt.Println(arr1)
// 二维数组的遍历
for i := 0; i < len(arr1); i++ {
for j := 0; j < len(arr1[i]); j++ {
fmt.Printf("%d ", arr1[i][j])
}
fmt.Print("\n")
}
}
1 2 3
4 5 6
7 8 9
注意: 多维数组只有第一层可以使用[...]
来让编译器推导数组长度。例如:
//支持的写法
arr1 := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
//不支持多维数组的内层使用[...]
arr2 := [3][...]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
package main
import "fmt"
func main() {
arr4 := [...]int{2, 5, 8}
arr5 := arr4
arr5[1] = 666
fmt.Println(arr4, arr5)
fmt.Println(arr4 == arr5)
fmt.Println(arr4 != arr5)
}
[2 5 8] [2 666 8]
false
true
练习
1.求数组[1, 3, 5, 7, 8]所有元素的和
package main
import "fmt"
func main() {
arr1 := [...]int{1, 3, 5, 7, 8}
var count int
for _, value := range arr1 {
count += value
}
fmt.Println("数组arr1的元素之和为:", count)
}
数组arr1的元素之和为: 24
2.找出数组中和为指定值的两个元素的下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的下标分别为(0,3)和(1,2)。
package main
import "fmt"
func main() {
arr1 := [...]int{1, 3, 5, 7, 8}
for i, v := range arr1 {
for j := i + 1; j < len(arr1); j++ {
if v+arr1[j] == 8 {
fmt.Printf("(%d %d)\n", i, j)
}
}
}
}
(0 3)
(1 2)
(silce)切片
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。
声明切片
package main
func main() {
var s []int
}
package main
import "fmt"
func main() {
s1 := []int{1, 3, 5}
s2 := []string{"西安", "北京", "上海"}
fmt.Println(s1, s2)
fmt.Println(s1 == nil) // nil表示空的意思
}
[1 3 5] [西安 北京 上海]
false
切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。
容量:切片的容量就是底层数组第一个元素到最后一个元素。
package main
import "fmt"
func main() {
// 切片的长度和容量
s3 := []string{"西安", "北京", "上海"}
fmt.Println("切片的长度是:", len(s3), "切片的容量是:", cap(s3))
}
切片的长度是: 3 切片的容量是: 3
package main
import "fmt"
func main() {
arr1 := []int{1, 5, 7, 8, 9, 30, 50, 77, 666}
s1 := arr1[1:4]
s2 := arr1[:3]
s3 := arr1[3:]
s4 := arr1[:]
arr1[3] = 999
fmt.Println(s1, len(s1), cap(s1))
fmt.Println(s2, len(s2), cap(s2))
fmt.Println(s3, len(s3), cap(s3))
fmt.Println(s4, len(s4), cap(s4))
}
[5 7 999] 3 8
[1 5 7] 3 9
[999 9 30 50 77 666] 6 6
[1 5 7 999 9 30 50 77 666] 9 9
make函数创建切片
我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()
函数,格式如下:
make([]T, size, cap)
-
T:切片的元素类型
-
size:切片中元素的数量
-
cap:切片的容量
package main
import "fmt"
func main() {
// 使用make函数创建切片
s1 := make([]int, 5, 10) // 类型、长度、容量(容量若不写,默认等于长度的值)
fmt.Println(s1, len(s1), cap(s1))
}
[0 0 0 0 0] 5 10
上面代码中s1
的内部存储空间已经分配了10个,但实际上只用了5个。 容量并不会影响当前元素的个数,所以len(s1)
返回5,cap(s1)
则返回该切片的容量10。
切片的本质
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
切片不能直接比较
切片之间是不能比较的,我们不能使用==
操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil
比较。 一个nil
值的切片并没有底层数组,一个nil
值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil
。
package main
import "fmt"
func main() {
var s1 []int
s2 := make([]int, 0, 0)
fmt.Println(s2, len(s2), cap(s2))
fmt.Println(s1 == nil, s2 == nil)
}
[] 0 0
true false
所以要判断一个切片是否是空的,要是用len(s2) == 0
来判断,不应该使用s2 == nil
来判断。
切片的切割
切割前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容。
package main
import "fmt"
func main() {
a1 := []int{23, 45, 12, 66, 77, 99}
s3 := a1[2:]
s4 := a1[:4]
a1[3] = 6666
fmt.Println(a1, s3, s4)
}
[23 45 12 6666 77 99] [12 6666 77 99] [23 45 12 6666]
切片的遍历
package main
import "fmt"
func main() {
a1 := []int{23, 45, 12, 66, 77, 99}
s3 := a1[:]
a1[3] = 6666
// 方式1
for i := 0; i < len(s3); i++ {
fmt.Println(s3[i])
}
fmt.Println()
// 方式2
for index, value := range s3 {
fmt.Println(index, value)
}
}
23
45
12
6666
77
99
0 23
1 45
2 12
3 6666
4 77
5 99
切片的扩容(append )
package main
import "fmt"
func main() {
s1 := []string{"阿拉斯加", "哈士奇", "萨摩耶", "大黄", "小黑"}
s1 = append(s1, "老王") // 调用append函数必须使用原来的变量进行接收
s1 = append(s1, "老张", "老李") // 调用append函数必须使用原来的变量进行接收
s2 := []string{"王大审", "张大妈", "李大坡"}
s1 = append(s1, s2...) // ...在Go表示打散,类似于python中的*
fmt.Println(s1)
}
[阿拉斯加 哈士奇 萨摩耶 大黄 小黑 老王 老张 老李 王大审 张大妈 李大坡]
注意:调用append函数建议使用原来的变量进行接收
提示:
...
在Go表示打散,类似于python中的
copy函数
Go语言内建的copy()
函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()
函数的使用格式如下:
copy(destSlice, srcSlice []T)
-
srcSlice: 数据来源切片
-
destSlice: 目标切片
package main
import "fmt"
func main() {
s1 := []string{"阿拉斯加", "哈士奇", "萨摩耶", "大黄", "小黑"}
s2 := make([]string, 5, 10)
copy(s2, s1) // 将切片s1中的元素复制到s2中
fmt.Println(s1)
fmt.Println(s2)
s1[1] = "哈哥"
fmt.Println(s1)
fmt.Println(s2)
}
[阿拉斯加 哈士奇 萨摩耶 大黄 小黑]
[阿拉斯加 哈士奇 萨摩耶 大黄 小黑]
[阿拉斯加 哈哥 萨摩耶 大黄 小黑]
[阿拉斯加 哈士奇 萨摩耶 大黄 小黑]
从切片中删除元素
Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。
package main
import "fmt"
func main() {
// 从切片中删除元素
arr1 := [...]int{1, 2, 3, 4, 5, 6, 7} // 数组
s1 := arr1[:] // 切片
// 要删除的元素索引为2
s1 = append(s1[:2], s1[3:]...)
fmt.Println(arr1) // append会修改底层的数组
fmt.Println(s1)
}
[1 2 4 5 6 7 7]
[1 2 4 5 6 7]
注意:append会修改底层的数组。
总结:要从切片a中删除索引为
index
的元素,操作方法是a = append(a[:index], a[index+1:]...
练习
1.写出下面代码的结果
package main
import (
"fmt"
)
func main() {
var a = make([]int, 5, 10)
fmt.Println(a)
for i := 0; i < 10; i++ {
a = append(a, i)
}
fmt.Println(a)
}
[0 0 0 0 0]
[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]
package main
import (
"fmt"
)
func main() {
var a = make([]string, 5, 10)
fmt.Println(a)
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i))
}
fmt.Println(a)
}
[ ]
[ 0 1 2 3 4 5 6 7 8 9]
2.使用内置的sort
包对数组var a = [...]int{3, 7, 8, 9, 1}
进行排序
package main
import (
"fmt"
"sort"
)
func main() {
s6 := []int{3, 7, 8, 9, 1}
sort.Ints(s6)
fmt.Println(s6)
}
[1 3 7 8 9]
指针
任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。
比如,“我喜欢你”这句话,我想把它写入程序中,程序一启动这句话是要加载到内存(假设内存地址0x123456),我在程序中把这段话赋值给变量A
,把内存地址赋值给变量B
。这时候变量B
就是一个指针变量。通过变量A
和变量B
都能找到“我喜欢你”这句话。
Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&
(取地址)和*
(根据地址取值)。
package main
import (
"fmt"
"reflect"
)
func main() {
n := 18
p := &n // &符号表示取地址
fmt.Println(n)
fmt.Println(p, reflect.TypeOf(p)) // *int表示int类型的指针
m := *p // *表示根据地址取值
fmt.Println(m)
}
18
0xc0000aa058 *int
18
总结: 取地址操作符
&
和取值操作符*
是一对互补操作符,&
取出地址,*
根据地址取出地址指向的值。变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
对变量进行取地址(&)操作,可以获得这个变量的指针变量。
指针变量的值是指针地址。
对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
new和make
new
使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。
package main
import (
"fmt"
)
func main() {
// var a *int // a表示一个int类型的空指针
a := new(int) // 表示给a开辟内存空间
*a = 666 // 根据a的内存地址给a赋值
fmt.Println(*a)
fmt.Println(&a)
}
666
0xc000006028
开始的代码中var a *int
只是声明了一个指针变量a但是没有初始化,指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值。应该按照如下方式使用内置的new函数对a进行初始化之后就可以正常对其赋值了
make
make也是用于内存分配的,区别于new,它只用于slice、map以及channel的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
new与make的区别
二者都是用来做内存分配的。
make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
而new用于值类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。