09.指针(pointer)
2023年1月6日约 1252 字大约 4 分钟
09.指针(pointer)
1,指针
指针是一个代表着某个内存地址的值。
这个内存地址往往是在内存中存住的两一个变量的值的起始位置。Go语言对指针的支持介于Java语言和C/C++语言之间,它既没有像Java语言那样取消了代码对指针的直接操作的能力,也避免了C/C++语言中由于对指针的滥用而造成的安全和可靠性问题。
Go语言虽然保留了指针,但与其他编程语言不同的是:
- 默认值nil,没有NULL常量。
- 操作符"&"取变量地址,"*"通过指针访问目标对象。
- 不支持指针运算,不支持"->"运算符,直接用"."访问目标成员。
package main
import "fmt"
func main() {
var a int = 10
//每个变量有2层含义,变量的内存和变量的地址
fmt.Printf("a = %d\n", a) //a,变量的内存,也就是存在内存当中的内容
fmt.Printf("&a = %v\n", &a) //&a,变量的地址,也就是内存所在内存当中的位置,也叫指针
}
1,指针变量的基本使用
package main
import "fmt"
func main() {
var a int = 10
//每个变量有2层含义,变量的内存和变量的地址
fmt.Printf("a = %d\n", a) //a,变量的内存,也就是存在内存当中的内容
fmt.Printf("&a = %v\n", &a) //&a,变量的地址,也就是内存所在内存当中的位置,也叫指针
//保存某个变量的地址,需要指针类型 *int 保存int的地址, **int则保存*int的地址
//定义一个变量p,类型为*int
var p *int
p = &a //指针变量指向谁,就把谁的地址赋给指针变量
fmt.Printf("p = %v, &a = %v\n", p, &a)
*p = 666 //*p操作的不是p的内存,是p所指向的内存(就是a)
fmt.Printf("*p = %v, a = %v\n", *p, a)
}
以上是针对整形的一个测试,其实,在针对字符串的时候,也是一样的:
package main
import "fmt"
func main() {
s := "good bay"
var p *string = &s
*p = "ciao"
fmt.Printf("p = %v\n", p)
fmt.Printf("*p = %v\n", *p)
fmt.Printf("s = %v\n", s)
}
通过对 *p 赋另一个值来更改“对象”,这样 s 也会随之更改。
2,不要操作没有合法指向的内存
package main
import "fmt"
func main() {
var p *int
fmt.Println("p = ", p)
//*p = 666 //err,因为p没有合法指向。
var a int
p = &a //p指向a的指针
*p = 666
fmt.Println("a = ", a)
}
上边举了一个没有初始化值的变量,它会被缺省为nil。
但是注意:还不能得到一个文字或常量的地址。
常量:
package main
import "fmt"
func main() {
const i = 5
fmt.Printf("&i = ", &i) //err: cannot take the address of i
}
文字:
package main
import "fmt"
func main() {
fmt.Printf("test = ", &10) //err: cannot take the address of 10
}
所以说,Go 语言和 C、C++ 以及 D 语言这些低级(系统)语言一样,都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算(所谓的指针算法,如:pointer+2
,移动指针指向字符串的字节数或数组的某个位置)是不被允许的。Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。
因此 c = *p++
在 Go 语言的代码中是不合法的。
2,new函数
函数new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块儿内存空间,然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,返回的指针类型为*T。
1,new函数的使用
package main
import "fmt"
func main() {
/*之前的传统赋值方式
a := 10
var p *int
p = &a //指向一个合法内存
*p = 666
fmt.Println("*p =", *p)
*/
//p为*int类型,指向匿名int变量
var p *int
//new表示分配一个int类型的空间,给到变量p
p = new(int)
fmt.Println("*p =", *p) //*p=0
//同样,也可以使用自动推导类型进行赋值
q := new(int)
*q = 777
fmt.Println("*q =", *q)
}
我们只需要使用new()函数,无需担心其内存的生命周期或怎样将其删除,因为Go语言的内存管理系统会帮我们打理一切(相当于自动GC)。
2,普通变量做函数的参数
package main
import "fmt"
func swap(a, b int) {
a, b = b, a
fmt.Printf("swap : a= %d, b= %d\n", a, b)
}
func main() {
a, b := 10, 20
swap(a, b) //通过变量进行值的传递(站在变量的角度上)
fmt.Printf("main : a= %d, b= %d\n", a, b)
}
3,指针做函数参数
package main
import "fmt"
func swap(p1, p2 *int) {
*p1, *p2 = *p2, *p1
}
func main() {
a, b := 10, 20
//通过一个函数交换a和b的内容
swap(&a, &b) //地址传递
fmt.Printf("main : a= %d, b= %d\n", a, b)
}
Powered by Waline v2.9.1