Go基础2

本文详细介绍了Go语言的基础知识,包括算术、关系、逻辑和位运算符,以及赋值运算符的使用。接着讲解了流程控制,如if、for循环以及特殊写法,并探讨了switchcase和goto语句。然后深入介绍了数组的定义、初始化、遍历和多维数组。最后,文章提到了切片的概念、创建、遍历和操作,以及指针的使用。
摘要由CSDN通过智能技术生成

目录

运算符

算术运算符

关系运算符

逻辑运算符

位运算符

赋值运算符

流程控制

if判断

if判断的特殊写法

for循环

死循环

for range 循环

练习

break和continue

switch case

goto(跳转到指定的标签)

(Array)数组

数组定义

数组的初始化

数组的遍历

多维数组

多维数组的遍历

数组是值类型

练习

(silce)切片

声明切片

make函数创建切片

切片的本质

切片不能直接比较

切片的切割

切片的遍历

切片的扩容(append )

copy函数

从切片中删除元素

练习

指针

new和make

new

make

new与make的区别


运算符

运算符用于在程序运行时执行数学或逻辑运算。

Go 语言内置的运算符有:

  1. 算术运算符

  2. 关系运算符

  3. 逻辑运算符

  4. 位运算符

  5. 赋值运算符

算术运算符

运算符描述
+相加
-相减
*相乘
/相除
%求余

注意: ++(自增)和--(自减)在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语言中最常用的流程控制有iffor,而switchgoto主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。

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也必须与上一个ifelse 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循环可以通过breakgotoreturnpanic语句强制退出循环。

for range 循环

Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:

  1. 数组、切片、字符串返回索引和值。

  2. map返回键和值。

  3. 通道(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-言

练习

  1. 九九乘法表
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的区别

  1. 二者都是用来做内存分配的。

  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;

  3. 而new用于值类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值