Go语言中的指针
在GO语言中指针是不能进行运算和偏移,是安全指针 取地址操作符&和取值操作符*是一对互补操作符: &取出地址 *根据地址取出地址指向的值。
对变量进行取地址(&)操作,可以获得这个变量的指针变量。 指针变量的值是指针地址。 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值
1.标准写法
/*
取内存地址符:&
根据内存地址取值:*
*/
package main
import "fmt"
func main() {
a := 101
b := &a
c := *b
fmt.Println(a,b,c)
}
复制代码
运行结果:101 0xc00000a098 101
2、编写一个指针函数,传入的所有内存值都会输出成函数值
package main
import "fmt"
func modify1(x int) { //定义函数
x =101
}
func modify2(y *int) { //定义函数含指针类型的参数
*y = 102
}
func main() {
a := 100
b :=200
modify2(&a) //这里必须传内存地址,要不然下面函数modify2取不到值,因为必须传内存地址,才能转成实际值
modify2(&b)
fmt.Println(a) //经过上面的转化,a值从100变成了102
fmt.Println(b)
}
复制代码
3.new
func main() {
var a = new(int) //new是可以初始化指针的参数,初始化之后才能赋值。a定义成一个int类型的指针
*a = 10
fmt.Println(a) //这样输出相当于下面的&b,只不过这里使用了new函数定义后,就不需要加&
fmt.Printf("%T\n",a)
fmt.Println(*a)
b := 20
fmt.Printf("%T\n",b)
fmt.Println(&b)
}
复制代码
初始化 使用new函数可初始化指针
package main
import "fmt"
var a *int
func main() {
*a = 100
fmt.Println(a)
}
复制代码
运行结果:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x67798b]
goroutine 1 [running]:
main.main()
D:/go project/test/for.go:7 +0x2b
exit status 2
复制代码
上面代码因为指针没有初始化,导致内存指定是一个空类型。无法赋值
下面使用new函数初始化指针
package main
import "fmt"
var a *int
func main() {
a = new(int)
*a = 100
fmt.Println(*a)
}
复制代码
new和make都是用于初始化的,它们两个区别在于new只能初始化值类型(int、string等),make只能初始化引用类型(切片、map、channel)
Go语言中的数组
序言
Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。
数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推。数组的下标取值范围是从0开始,到长度减1。
数组一旦定义后,大小不能更改。
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。
相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于扩展。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
声明数组和初始化数组
数组在声明时需要指定数组中存储的数据类型和数组的大小。
var city [3]string
var city = [3]string{"beijing","hebei","tianjin"}
/取值, 修改值
fmt.Println(course[0])
//修改值
course[0] = "django3"
fmt.Println(course)
//数组的另一种创建方式
//var a [4] float32
var a = [4]float32{1.0}
fmt.Println(a)
var c = [5] int{'A', 'B'}
fmt.Println(c)
//首次接触到了省略号
d := [...]int{1,2,3,4,5}
fmt.Println(d)
e := [5]int{4:100}
fmt.Println(e)
f := [...]int{0:1, 4:1, 9:100}
fmt.Println(f)
复制代码
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。 数组的其他创建方式:
var a [4] float32 // 等价于:var arr2 = [4]float32{}
fmt.Println(a) // [0 0 0 0]
var b = [5] string{"ruby", "王二狗", "rose"}
fmt.Println(b) // [ruby 王二狗 rose ]
var c = [5] int{'A', 'B', 'C', 'D', 'E'} // byte
fmt.Println(c) // [65 66 67 68 69]
d := [...] int{1,2,3,4,5} // 根据元素的个数,设置数组的大小
fmt.Println(d)//[1 2 3 4 5]
e := [5] int{4: 100} // [0 0 0 0 100]
fmt.Println(e)
f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1]
fmt.Println(f)
复制代码
访问数组元素
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
复制代码
数组长度
通过将数组作为参数传递给len函数,可以获得数组的长度。
package main
import "fmt"
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
fmt.Println("length of a is",len(a))
}
复制代码
你可以忽略声明中数组的长度并将其替换为…让编译器为你找到长度。这是在下面的程序中完成的。
遍历数组
使用range遍历数组
f := [...]int{0:1, 4:1, 9:100}
for i, value := range f {
fmt.Println(i, value)
}
//使用for range求和
sum := 0
for _, value := range f {
sum += value
}
fmt.Println(sum)
//使用for语句也可以遍历数组
sum = 0
for i := 0; i<len(f); i++{
sum += f[i]
}
fmt.Println(sum)
复制代码
数组是一个值类型
数组是值类型 Go中的数组是值类型,而不是引用类型。这意味着当它们被分配给一个新变量时,将把原始数组的副本分配给新变量。如果对新变量进行了更改,则不会在原始数组中反映。
数组的大小是类型的一部分。因此[5]int和[25]int是不同的类型。因此,数组不能被调整大小。不要担心这个限制,因为切片的存在是为了解决这个问题。
Go语言中的切片
什么是切片?
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
Go的切片类型为处理同类型数据序列提供一个方便而高效的方式。 切片有些类似于其他语言中的数组,但是有一些不同寻常的特性。 本文将深入切片的本质,并讲解它的用法。
切片的定义
// 第一种 var identifier []type
var s1 []int
s2 := []string
// 第二种 使用make
var slice1 []type = make([]type, len)
简写成
slice2 := make([]type,len,cap)
// 第三种 通过对数组操作从而创建一个切片
courses := [...]string{"ruby","php","java","go","python"}
subCourse := courses[1:2]
fmt.Printf("%T", subCourse)
复制代码
切片的初始化
创建 slice 的方式有以下几种:
序号 | 方式 | 代码示例 |
---|---|---|
1 | 直接声明 | var slice []int |
2 | new | slice := *new([]int) |
3 | 字面量 | slice := []int{1,2,3,4,5} |
4 | make | slice := make([]int, 5, 10) |
5 | 从切片或数组“截取” | slice := array[1:5] 或 slice := sourceSl |
切片的底层存储
go和python的切片区别
slice的扩容机制
Go语言中的map
map的创建
这里要注意,在Go语言中对于一个map,要求它的所有key的数据类型保持一致,所有value的数据类型保持一致,注: key和value可以是不同的数据类型
// 1. 字面值
m1 := map[string]string{
"m1":"v1" // 定义时指定初始的key/value 后面可以继续添加
}
// 2. 使用make函数
m2 := make(map[string]string) // 创建时里面不含元素,所有的元素都需要后续添加
m2["m2"] = "v2" 添加元素
// 3. 定义一个空的map
m3 := map[string]string{}
m4 := make(map[string]string)
复制代码
map中key的类型
map中的每个key在keys的集合中是唯一的,而且需要支持 == or != 操作
key的常用类型:int, rune, string, 结构体(每个元素需要支持 == or != 操作), 指针, 基于这些类型自定义的类型
// m0 可以, key类型为string, 支持 == 比较操作
{
var m0 map[string]string // 定义map类型变量m0,key的类型为string,value的类型string
fmt.Println(m0)
}
// m1 不可以, []byte是slice,不支持 == != 操作,不可以作为map key的数据类型
{
//var m1 map[[]byte]string // 报错: invalid map key type []byte
//fmt.Println(m1)
// 准确说slice类型只能与nil比较,其他的都不可以,可以通过如下测试:
// var b1,b2 []byte
// fmt.Println(b1==b2) // 报错: invalid operation: b1 == b2 (slice can only be compared to nil)
}
// m2 可以, interface{}类型可以作为key,但是需要加入的key的类型是可以比较的
{
var m2 map[interface{}]string
m2 = make(map[interface{}]string)
//m2[[]byte("k2")]="v2" // panic: runtime error: hash of unhashable type []uint8
m2[123] = "123"
m2[12.3] = "123"
fmt.Println(m2)
}
// m3 可以, 数组支持比较
{
a3 := [3]int{1, 2, 3}
var m3 map[[3]int]string
m3 = make(map[[3]int]string)
m3[a3] = "m3"
fmt.Println(m3)
}
// m4 可以,book1里面的元素都是支持== !=
{
type book1 struct {
name string
}
var m4 map[book1]string
fmt.Println(m4)
}
// m5 不可以, text元素类型为[]byte, 不满足key的要求
{
// type book2 struct {
// name string
// text []byte //没有这个就可以
// }
//var m5 map[book2]string //invalid map key type book2
//fmt.Println(m5)
}
复制代码
map的增删改
// map的基本操作
m := map[string]string{
"a": "va",
"b": "vb",
"d":""
}
// 增加、修改
m["c"] = "vc"
m["b"] = "vb1"
fmt.Printf("%v\n", m)
// 查询 怎么知道返回的空字符串是没有获取到还是值本身就是这样的空字符串呢
v, ok := m["d"]
if ok {
fmt.Println("找到了", v)
}else{
fmt.Println("没找到")
}
fmt.Println(v, ok)
//删除
//delete(m, "a")
//delete(m, "e")
//delete(m, "a")
//fmt.Printf("%v", m)
// 遍历
for k, v := range m {
fmt.Println(k, v)
}
复制代码
数组、切片以及map的更多源码细节