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

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
相关推荐
@大迁世界7 分钟前
相信我兄弟:Cloudflare Rust 的 .unwrap() 方法在 330 多个数据中心引发了恐慌
开发语言·后端·rust
5***g29818 分钟前
新手如何快速搭建一个Springboot项目
java·spring boot·后端
2***B4491 小时前
Rust在系统编程中的内存安全
开发语言·后端·rust
U***e632 小时前
Rust错误处理最佳实践
开发语言·后端·rust
q***47182 小时前
Spring中的IOC详解
java·后端·spring
码事漫谈3 小时前
C++小白最容易踩的10个坑(附避坑指南)
后端
码事漫谈3 小时前
性能提升11.4%!C++ Vector的reserve()方法让我大吃一惊
后端
稚辉君.MCA_P8_Java3 小时前
Gemini永久会员 Java中的四边形不等式优化
java·后端·算法
稚辉君.MCA_P8_Java4 小时前
通义 插入排序(Insertion Sort)
数据结构·后端·算法·架构·排序算法
q***69774 小时前
【Spring Boot】统一数据返回
java·spring boot·后端