两个常用的符号
- &取地址符
- *取指针存的地址中的内容(指针中存的是某个内存地址,用*指针变量取出的是这个地址中存的内容)
指针的概念
指针=内存地址如果指针没有限制,那它可以指向内存中的任何位置
a内存地址 0x12345678, 值:100 var a int = 100
*b指针变量,指向了一个内存地址 0x12345678(a的内存地址)
指针变量b中存了变量a的地址。这个就是b指向了a
varaint=100
varb= &a
fmt.Println("a本身的内存地址:", &a)//0xc00008c0a8
fmt.Println("b内存中存储的地址:",b)//0xc00008c0a8
fmt.Println("b本身的内存地址:", &b)//0xc00008e060
fmt.Println("b内存中存储的地址中的值", *b)//100
此时a和b内存中存储的地址是指向的同一个内存空间,所以修改了*b a 和 *b都会改变(可以看做这时a == *b)
*b=2000
fmt.Println("a",a)
fmt.Println("*b", *b)
指针如何使用
变量定义
varaint=100
指针变量的定义 正规的定义方式
varb*int= &a
指针变量的定义 偷懒的方式
c:= &a
fmt.Println("a的值",a)//100
fmt.Println("a的内存地址", &a)//0xc00000a0e8
fmt.Println("b变量中存的地址",b)//0xc00000a0e8
fmt.Println("b变量自己的内存地址", &b)//0xc000054068
fmt.Println("b变量中存的地址中的值", *b)//100
fmt.Println("c变量中存的地址",c)//0xc00000a0e8
fmt.Println("c变量自己的内存地址", &c)//0xc000054070
fmt.Println("c变量中存的地址中的值", *c)//100
通过指针b或指针c都可以改变a的值
*c=123
fmt.Println(a)
多级指针的使用(套娃)
多级指针:指针的套娃,指针指向指针 , 指针类型 第一个*指针类型, *int是这个指针对应的类型
如何理解多个符号,第一个取出来后,后面就是它的类型 *(*(*(int)))
例如:*ptr3 的类型就是 **int **ptr3 的类型就是*int ***ptr的类型就是int
varaint=100
varptr1*int= &a
varptr2**int= &ptr1
varptr3***int= &ptr2
fmt.Println("a的内存地址", &a)//0xc00000a0e8
fmt.Println("-----------------------------------------------")
fmt.Println("ptr1变量中存的内存地址",ptr1)//0xc00000a0e8
fmt.Println("ptr1变量自己的内存地址", &ptr1)//0xc000054068
fmt.Println("-----------------------------------------------")
fmt.Println("ptr2变量中存的内存地址",ptr2)//0xc000054068
fmt.Println("ptr2变量中存的内存地址中存的内存地址", *ptr2)//0xc00000a0e8
fmt.Println("ptr2变量自己的内存地址", &ptr2)//0xc000054070
fmt.Println("-----------------------------------------------")
fmt.Println("ptr3变量中存的内存地址",ptr3)//0xc000054070
fmt.Println("ptr3变量中存的内存地址中存的内存地址", *ptr3)//0xc000054068
fmt.Println("ptr3变量中存的内存地址中存的内存地址中存的内存地址", **ptr3)//0xc00000a0e8
fmt.Println("ptr3变量自己的内存地址", &ptr3)//0xc000054078
fmt.Printf("%T\n", *ptr3)
fmt.Printf("%T\n", **ptr3)
fmt.Printf("%T\n", ***ptr3)
//这时候改变a值的方式就很多了 因为*ptr1 **ptr2 ***ptr3 和 a 都是等价的,因为操控的是同一片内存空间
*ptr1=2000
fmt.Println(a)
**ptr2=3000
fmt.Println(a)
***ptr3=4000
fmt.Println(a)
数组指针
数组指针,首先它是一个指针,然后指向了一个数组
定义一个数组
vararr= [4]int{1,2,3,4}
fmt.Println(arr)
fmt.Printf("数组的地址:%p\n", &arr)//0xc0000121c0
定义一个数组指针 指向数组
varptr_arr*[4]int= &arr
fmt.Println(ptr_arr)//&[1 2 3 4]
fmt.Println("取出内容:", *ptr_arr)//[1 2 3 4] 这时*ptr_arr == arr
fmt.Printf("ptr_arr变量中存的地址:%p\n",ptr_arr)//0xc0000121c0
fmt.Printf("ptr_arr变量自己的地址%p\n", &ptr_arr)//0xc000054070
通过指针操作数组 原生的操作方法
(*ptr_arr)[0] =100
fmt.Println(arr)//[100 2 3 4]
作为21世纪的新兴语言 不允许有这么的丑的代码出现
由于ptr_arr指向了arr 所以就出现了语法糖 可以像正常操作数组一样操作数组指针
ptr_arr[0] =123
fmt.Println(arr)
但这里需要注意的是,虽然因为语法糖的关系,可以用相同的方法操作。
但是它们的类型还是有本质区别的,一个是数组类型[4]int,一个是数组指针类型*[4]int
fmt.Printf("arr的类型%T\n",arr)//[4]int
fmt.Printf("ptr_arr的类型%T\n",ptr_arr)//*[4]int
指针数组
指针数组,首先它是一个数组,数组里的每个元素保存的都是指针
a:=100
b:=200
c:=300
d:=400
创建一个指针数组,数组中存放的都是变量的地址
注意数组指针跟指针数组的区别: 数组指针*[4]int 指针数组[4]*int
ptr_arr:= [4]*int{&a, &b, &c, &d}
fmt.Println(ptr_arr)//[0xc00008c0a8 0xc00008c0c0 0xc00008c0c8 0xc00008c0d0]
通过指针修改变量的值
fmt.Println(a,b,c,d)//100 200 300 400
*ptr_arr[0] =666因为数组中的每个元素都是指针,所以要取出每个值都需要在取出元素的时候在前面再加上*号
fmt.Println(a,b,c,d)//666 200 300 400
指针函数
首先需要是一个函数,这个函数的返回值是一个指针。
funcmain() {
调用了这个函数,可以得到一个指针类型的返回值
ptr:=fn()
fmt.Printf("ptr的类型:%T\n",ptr)//*[4]int
fmt.Printf("ptr的地址:%p\n", &ptr)//0xc00008e060
fmt.Println("ptr中的值:", *ptr)//[1 2 3 4]
回顾数组指针的操作方式(语法糖),我们就可以来愉快的操作这个数组了
fmt.Println((*ptr)[0])//原始的操作方式
ptr[0] =666//香香的语法糖
fmt.Println(ptr[0])
}
这个函数的返回值是一个指针
funcfn() *[4]int{
arr:= [4]int{1,2,3,4}
return&arr
}
指针作为函数参数
funcmain() {
a:=100
fmt.Printf("a变量的地址:%p\n", &a)//0xc00000a0e8
fmt.Printf("进入函数前 a的值 =%d\n",a)
updata1(a)
这里可以看出 因为值类型是拷贝的关系,并不会因为函数体内改变了a的值而改变a本身的值
fmt.Printf("函数结束后 a的值 =%d\n",a)
fmt.Printf("进入函数前 a的值 =%d\n",a)
updata2(&a)
这里可以看出 因为指针是引用类型传入的是地址,所以函数体内改变了a的值导致a本身的值也被改变了
fmt.Printf("函数结束后 a的值 =%d\n",a)
}
funcupdata1(aint) {
fmt.Println("--->",a)
fmt.Printf("a变量中的地址:%p\n", &a)//0xc00000a110
a=666
fmt.Println("--->",a)
}
funcupdata2(ptr*int) {
fmt.Println("--->", *ptr)
fmt.Printf("ptr变量中的地址:%p\n",ptr)//0xc00000a0e8
*ptr=666
fmt.Println("--->", *ptr)
}