go指针与数据类型
Golang指针
1.1 概念
指针,是一种数据类型,用于表示数据的内存地址。
//声明一个 字符串类型变量(默认初始化的为空字符串)
var v1 string
//声明一个 字符串的指针类型 的变量(默认初始化值为nil)
var v2 *string
//声明一个 字符串类型 的变量,值为 哈哈哈
var name string = "哈哈哈"
//声明一个 字符串的指针类型 的变量,值为name 对应的内存地址。
// *string用于表示string类型变量内存地址的类型
var p *string = &name

说明:指针与我们熟悉的数据类型一样,它也是一种数据类型,与普通数据类型的区别就是,它是数据比较特别,是内存地址的形式,比如 *string表示string类型的内存地址
1.2 指针的意义

通过指针指向内存地址,不管这块内存中存放的何值,当这块内存中值发生变化时,通过指针获取这块内存中的值也会同步发生变化。相当于创建了一个地址的引用,以后根据这个引用再去获取他里面的值。
1.3 使用场景
场景1:数据修改是否需要同步,需要同步就用指针,不需要同步就不用指针。
v1 := "hello kribee"
v2 := v1
v1 = "Najda"
fmt.Println(v1, v2)
//结果:Najda   hello kribee
v1 := "hello kribee"
v2 := &v1
v1 = "Najda"
fmt.Println(v1, *v2)
//结果:Najda   Najda
对应图示:

场景2:
package main
import "fmt"
func changeData(data string) {
	data = "嘿嘿嘿"
}
func main() {
	name := "kribee"
	
	//本质上会将name的值复制一份,并赋值给data
	changeData(name)
	fmt.Println(name)
    //结果:kribee
}
package main
import "fmt"
func changeData(p *string) {
	*p = "嘿嘿嘿"
}
func main() {
	name := "kribee"
	
	changeData(&name)
	fmt.Println(name)
    //结果:嘿嘿嘿
}
场景3:
package main
import "fmt"
func main() {
	var username string
    fmt.Printf("请输入用户名:")
    
    fmt.Scanf("%s", &username)
    
    if username == "嘿嘿嘿" {
        fmt.Printf("登录成功")
    }else {
        fmt.Printf("登录失败")
    }
	
}
场景3说明:"var username string" 声明一个username变量,初始值为空字符串。“fmt.Scanf("%s", &username)” 中“ &username”将username的内存地址传入Scanf函数中,当用户输入值后,会将输入的值赋值到内存地址对应的内存中,username变量的就有值了。
1.4 指针的指针
name := "hello kribee"
//声明一个指针变量p1,内部存储name的内存地址
var p1 *string = &name
//声明一个指针的指针类型变量p2,内部存储指针p1的内存地址
var p2 **string = &p1
//声明一个指针的指针的指针类型变量p3,内部存储指针p2的内存地址
var p3 ***string = &p2
图示:

说明:指针的指针的指针...其实意义不大,本例想说明,指针本质上是指向某一个值的内存地址
代码:
package main
import (
	"fmt"
)
func main() {
	var name string = "dj"
	var p1 *string = &name
	var p2 **string = &p1
	var p3 ***string = &p2
	fmt.Println(name, &name)
	fmt.Println(p1, &p1)
	fmt.Println(p2, &p2)
	fmt.Println(p3, &p3)
}
//结果:
dj 0xc00003c1f0
0xc00003c1f0 0xc000006028
0xc000006028 0xc000006030
0xc000006030 0xc000006038
1.5 指针的高级操作
- 数组的地址 == 数组的第一个元素的地址

图说明:&dataList和&dataList[0]指针的值是相同的,但指针类型不同
package main
import "fmt"
func main() {
	dataList := [3]int8{11, 22, 33}
	//注意与C语言的区别,C语言中数据名称就代表数组地址,也是数据第一个元素的地址,即直接是dataList, 而不会再&dataList
	fmt.Printf("数组的地址为:%p; 数组第一个元素的地址为:%p", &dataList, &dataList[0])
	//&dataList 和 &dataList[0] 的内存中存储的数据虽然相同,但他们是两个不同类型的指针
	//&dataList是 *[3]int8 类型
	//&dataList[0]是*int8类型
}
- 指针的运算
package main
import (
	"fmt"
	"unsafe"
)
func main() {
	dataList := [3]int8{11, 22, 33}
	//1. 获取数组第一个元素的地址
	var firstDataPtr *int8 = &dataList[0]
	//2. 转换成Pointer类型
	ptr := unsafe.Pointer(firstDataPtr)
	//3. 转换成uintptr类型,然后进行内存地址的计算(即:地址加1个字节,意味着取第2个索引的值)
	targetAddress := uintptr(ptr) + 1
	//4. 将新地址重新转换成Pointer类型
	newPtr := unsafe.Pointer(targetAddress)
	//5. Pointer对象转换成int8指针类型
	value := (*int8)(newPtr)
}
//结果为:22
值类型与引用类型
1)值类型:基本数据类型int系列,float系列,bool, string, 数组,结构体struct
2)引用类型,指针,slice切片,map,管道chan,interface等都是引用类型
值类型与引用类型使用特点:
1)值类型:变量直接存储值,内存通常在栈中分配

2)引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值)内存通常
在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。

3)内存的栈区和堆区示意图

 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号