![cover](https://img-blog.csdnimg.cn/img_convert/16da6262399c40c0a4a3f873eb05d63e.png)
Golang | 指针的使用
在面对 Java、C#、VB 这些高级程序设计语言的时候,很少有听到指针的概念。所以长期接触这类语言的开发者,在接触 Go 的时候,基础语法中最不能理解的应该就是指针了。
在面对 Java、C#、VB 这些高级程序设计语言的时候,很少有听到指针的概念。所以长期接触这类语言的开发者,在接触 Go 的时候,基础语法中最不能理解的应该就是指针了。
Java 中的指针
Java 中没有明确的指针的概念。Java 帮我们封装了对象,简化了对象之间引用的关系。
此外,Java 是值传递。在进行值传递的时候,真正传递的是一个 Java 对象的引用。
/**
* @author Real
* @since 2023/11/27 22:02
*/
@Data
public class User {
private String name;
private int age;
}
首先我们定义一个 User 对象,我们定义一个对 User 方法修改名称的方法。
public static void renameUser(User user, String newName) {
if (user == null) {
return;
}
user.setName(newName);
}
那么在调用的时候,我们需要传进一个 User 和 newName 变量。
/**
* @author Real
* @since 2023/11/27 22:02
*/
@Data
@AllArgsConstructor
public class User {
private String name;
private int age;
public static void renameUser(User user, String newName) {
if (user == null) {
return;
}
user.setName(newName);
}
}
调用测试:
public static void main(String[] args) {
User user = new User("testUser", 20);
// user: User(name=testUser, age=20); hashcode: -1146741326
System.out.println("user: " + user + "; hashcode: " + user.hashCode());
User.renameUser(user, "Real");
// user: User(name=Real, age=20); hashcode: 2547699
System.out.println("user: " + user + "; hashcode: " + user.hashCode());
}
可以看到运行的结果如注释所示。但是如果在传入的 User 对象中,对 User 重新赋值,结果会是怎样呢?
① 修改原 rename 方法,将 user 对象重新赋值。
public static void renameUser(User user, String newName) {
if (user == null) {
return;
}
user = new User("newUser", 18);
user.setName(newName);
}
② 再次执行测试代码:
可以看到 User 对象并没有重新变化。
Java 中关于值传递和引用传递的讨论可以参考:https://www.zhihu.com/question/31203609/answer/576030121
结合上面的实验以及各种解析,我们可以得出结论:
- Java 中只有值传递,没有引用传递。将对象作为形参传递,实际传递的是实参的引用地址。
- 反映在编码中,方法中将形参对象重新指向后修改,这个值不会反映到实参上。
Go 中的指针
在 Go 中,是有明确的指针类型数据的。
func pointer() {
str := "Golang"
// p 是指向 str 的指针
var p *string = &str
// 修改了 p, str 的值也发生了改变
*p = "Go语言"
fmt.Println(str)
}
Go 语言里面的指针,明确的就是指向数据的地址值。
这里做一个简单的小测验。我们针对简单的数据类型,进行分别进行值传递和引用传递(指针)。查看运行结果,可以发现运用指针的引用传递,最终的结果才是符合预期的。
func pointerTest() {
// 100,num 没有变化
num := 100
add(num)
fmt.Println(num)
// 101,指针传递,num 被修改
realAdd(&num)
fmt.Println(num)
}
func add(num int) {
num += 1
}
func realAdd(num *int) {
*num += 1
}
在上面的 add 方法中,传进来的 num 只是个形参,只是 pointerTest 方法中定义的 num 变量的一个副本。在 add 方法中对这个形参副本进行修改,最终是影响不到实际值的。
而 realAdd 方法,则针对的是指向 num 变量的指针,对这个指针指向的值进行修改,那么最终影响到的也会是这个指针指向的值。
Go 指针的使用
Go 语言中对于指针的操作,开发中使用到的非常有限。
- 定义指针:
var numPointer *int = &num
- 获取指针指向的值:
numberPointValue := *numPointer
// 对普通变量进行 & 操作,返回指针
var pointer *type = &(variable)
// 对指针进行 * 操作,返回指针指向的变量,该变量的值等于指向的变量的值
variableValue := *pointer
运行下列程序。
func numberPointer() {
num := 100
var numPointer *int = &num
numberPointValue := *numPointer
fmt.Printf("numberPointValue: %d, num == numberPointValue: %t\n", numberPointValue, num == numberPointValue)
fmt.Printf("num: %d, numberPointer: %p, numberPointer value: %d\n", num, numPointer, *numPointer)
}
运行结果:
numberPointValue: 100, num == numberPointValue: true
num: 100, numberPointer: 0xc0000aa170, numberPointer value: 100
Go 指针的注意事项
一个指针变量可以指向任何一个值的内存地址。它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,与它所指向的值的大小无关。
指针的书写
在书写表达式类似 var p *type
时,切记在 *
号和指针名称间留有一个空格,因为 var p*type
是语法正确的,但是在更复杂的表达式中,它容易被误认为是一个乘法表达式。
符号 *
可以放在一个指针前,如 *intPointer
,那么它将得到这个指针指向地址上所存储的值。这被称为反引用(或者内容或者间接引用)操作符;另一种说法是指针转移。
func pointerTestAssignment() {
var i1 = 5
fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1)
var intP *int
intP = &i1
fmt.Printf("The value at memory location %p is %d\n", intP, *intP)
}
这段代码的运行结果如下:
An integer: 5, its location in memory: 0xc0000161c8
The value at memory location 0xc0000161c8 is 5
这里面 i1
的内存地址,可以用下图来表示。
指针的运用
- 不能得到一个常量或者基础值字面量的地址:
const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10
- 对一个空指针的反向引用是不合法的,并且会使程序崩溃:
package main
func main() {
var p *int = nil
*p = 0
}
// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/>
// runtime error: invalid memory address or nil pointer dereference
Reference
更多推荐
所有评论(0)