一文看懂!编程语言访问变量指针和复制值

1 简介

当使用允许访问指针值的编程语言(如 Java、C#、C/C++、Go 等)时,通常必须小心区分按值传递和按引用传递。 本文通过概念和示例说明如何访问指针和复制值。

markdown 复制代码
                按值传递                            按引用传递
        将函数参数的值复制到另一个变量的机制         将实际参数传递给函数的机制
        在函数中所做的更改不会反映在原始值中        在函数中所做的更改反映在原始值
        复制实际参数                       实际参数的地址传递给函数
        函数获取实际内容的副本                 函数访问原始变量的内容
        需要更多内存                         需要较少内存
        需要更多时间,因为它涉及复制值           由于没有复制,因此需要的时间更少

分别看看结构体和指针数组在按值和按引用传递时的区别。Go 语言里这两个类型都有明显的行为差异,非常适合用来理解这个概念。

csharp 复制代码
	按值传递:使用按值传递时,函数参数的值将复制到内存中的另一个位置。在该函数中,仅访问或修改变量的副本。因此,原始值不受影响。
	Passing by reference:使用按引用传递时,将向该函数提供内存地址。换句话说,该函数可以访问 real 变量。

2 结构体(struct)

  • 按值传递结构体

函数接收的是结构体的副本。修改不会影响原始结构体。

go 复制代码
		package main

		import "fmt"

		type Person struct {
		    name string
		    age  int
		}

		func changePerson(p Person) {
		    p.age = 99
		    p.name = "Changed"
		}

		func main() {
		    p := Person{name: "Tom", age: 30}
		    changePerson(p)
		    fmt.Println("main:", p) // 输出: main: {Tom 30}
		}
  • 按引用传递结构体(传指针)

函数接收的是结构体的地址。修改会影响原始结构体。

css 复制代码
		func changePersonByRef(p *Person) {
		    p.age = 99
		    p.name = "Changed"
		}

		func main() {
		    p := Person{name: "Tom", age: 30}
		    changePersonByRef(&p)
		    fmt.Println("main:", p) // 输出: main: {Changed 99}
		}

2 针数组(数组中存的是指针)

这是一个有点绕的场景,但也非常常见。我们来仔细看一下。

  • 1 指针数组按值传递(传的是数组副本)

    go 复制代码
     func changeArray(arr [2]*int) {
         x := 100
         arr[0] = &x // 改变了指针指向
     }
    
     func main() {
         a, b := 1, 2
         arr := [2]*int{&a, &b}
         changeArray(arr)
         fmt.Println(*arr[0]) // 输出: 1,未改变原数组内容
     }

虽然修改了 arr[0] 的指针,但只是副本,原数组没变。

    1. 指针数组按引用传递(传指针数组的指针)

      go 复制代码
                 func changeArrayByRef(arr *[2]*int) {
                     x := 100
                     arr[0] = &x // 修改了原始数组中的指针
                 }
      
                 func main() {
                     a, b := 1, 2
                     arr := [2]*int{&a, &b}
                     changeArrayByRef(&arr)
                     fmt.Println(*arr[0]) // 输出: 100,改变了原数组内容
                 }
             
    1. 修改指针数组中指向的值(不改指向,只改值)

      css 复制代码
         func modifyPointedValue(arr [2]*int) {
             *arr[0] = 999
         }
      
         func main() {
             a, b := 1, 2
             arr := [2]*int{&a, &b}
             modifyPointedValue(arr)
             fmt.Println(a) // 输出: 999,值被改变
         }

虽然数组本身是按值传递,但指针指向的是同一个地址,对值的修改仍然生效。

3 小结

按值传递是指将函数参数值复制到另一个变量的机制,而按引用传递是指将实际参数传递给函数的机制。因此,这是按值传递和按引用传递之间的主要区别。

markdown 复制代码
        类型	   是否需要指针来传引用	    修改指针本身	修改指向的值
        结构体  	    ✅	                  ✅	                ✅
        指针数组         ✅(如果改数组内容)  ✅	             不需要
        数组中的指针值	不需要	          ---	             ✅

如果需要处理较大的结构体,推荐用指针传参,减少复制开销。

参考:

ruby 复制代码
https://go.dev/tour/basics/11 https://go.dev/doc/effective_go
相关推荐
coderSong25682 小时前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy3 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
咖啡啡不加糖4 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
大鸡腿同学5 小时前
纳瓦尔宝典
后端
2302_809798326 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
zhojiew7 小时前
关于akka官方quickstart示例程序(scala)的记录
后端·scala
sclibingqing7 小时前
SpringBoot项目接口集中测试方法及实现
java·spring boot·后端
JohnYan8 小时前
Bun技术评估 - 03 HTTP Server
javascript·后端·bun
周末程序猿8 小时前
Linux高性能网络编程十谈|C++11实现22种高并发模型
后端·面试