Go Basics 2

Table of contents

operator

arithmetic operator

relational operator

Logical Operators

bitwise operator

assignment operator

process control

if judgment

Special way of writing if judgment

for loop

Infinite loop

for range loop

practise

break和continue

switch case

goto (jump to the specified label)

(Array) array

array definition

Array initialization

array traversal

Multidimensional Arrays

Traversal of multidimensional arrays

Arrays are value types

practise

(silce) slice

declare slice

The make function creates slices

The nature of slices

Slices are not directly comparable

cutting of slices

slice traversal

Slice expansion (append)

copy function

remove elements from slice

practise

pointer

new and make

new

make

The difference between new and make


operator

Operators are used to perform mathematical or logical operations while the program is running.

The built-in operators in Go language are:

  1. arithmetic operator

  2. relational operator

  3. Logical Operators

  4. bitwise operator

  5. assignment operator

arithmetic operator

operator describe
+ add up
- Subtract
* multiplied
/ to divide
% Surplus

Note: ++ (increment) and --(decrement) are separate statements in Go language, not operators.

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

relational operator

operator describe
== Checks if two values ​​are equal and returns True if they are equal, False otherwise.
!= Checks if two values ​​are not equal and returns True if not, False otherwise.
> Checks whether the value on the left is greater than the value on the right, and returns True if it is, False otherwise.
>= Checks whether the value on the left is greater than or equal to the value on the right, and returns True if it is, otherwise returns False.
< Checks if the value on the left is less than the value on the right, returning True if it is, False otherwise.
<= Checks whether the value on the left is less than or equal to the value on the right, and returns True if it is, otherwise returns 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

Logical Operators

operator describe
&& Logical and operator. True if both operands are True, otherwise False.
|| Logical or operator. True if both operands have a True, otherwise False.
! Logical not operator. If the condition is True, it is False, otherwise it is 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

bitwise operator

operator describe
& Binary phase AND of the corresponding two numbers involved in the operation. (Both are 1 to be 1)
| Binary phase OR corresponding to the two numbers involved in the operation. (If one of the two bits is 1, it is 1)
^ The corresponding binary bits of the two numbers participating in the operation are different or, when the two corresponding binary bits are different, the result is 1. (If the two bits are different, it is 1)
<< Shifting left by n bits is multiplying by 2 to the nth power. "a<<b" is to shift all the binary bits of a to the left by b bits, discard the high bits, and fill the low bits with 0.
>> Shifting right by n bits is dividing by 2 to the nth power. "a>>b" is to shift all the binary bits of a to the right by b bits.
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

assignment operator

operator describe
= Simple assignment operator that assigns the value of an expression to an lvalue
+= Add and then assign
-= Subtract and then assign
*= Multiply and assign
/= assign after division
%= assign after remainder
<<= assignment after left shift
>>= assignment after right shift
&= bitwise and post-assignment
|= bitwise or post-assignment
^= Assignment after bitwise XOR
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

process control

Flow control is an important part of each programming language to control the logic direction and execution order. The most commonly used flow control in Go language is sum, and sum ifis formainly switcha gotostructure created to simplify code and reduce repetitive code. It belongs to the extended class of flow control .

if judgment

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("好好学习,天天上当")
	}
}

你已经成年了!
年轻就是好

The Go language stipulates that ifthe matching left parenthesis { must if和表达式be placed on the same line as and, { otherwise it will trigger a compilation error. In the same way, elsethe matching { must also be elsewritten on the same line as and, elseand it must also be on the same line as the previous ifor else ifright curly brace.

Special way of writing if judgment

There is also a special way of writing the if conditional judgment. You can add an execution statement before the if expression, and then judge according to the variable value. For example:

func ifDemo2() {
	if score := 65; score >= 90 {
		fmt.Println("A") 
	} else if score > 75 {
		fmt.Println("B")
	} else {
		fmt.Println("C")
	}
}

for loop

All loop types in Go language can forbe done using keywords.

The basic format of a for loop is as follows:

for 初始语句;条件表达式;结束语句{
    循环体语句
}

When the conditional expression returns, truethe loop body loops continuously until the conditional expression returns and falseautomatically exits the loop.

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

The initial statement of the for loop can be ignored, but the semicolon after the initial statement must be written, for example:

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用于值类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

Guess you like

Origin blog.csdn.net/hpl980342791/article/details/125383794