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

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,未改变原数组内容
     }

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

    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 分钟前
基于springboot的农资管理系统-附源码
java·spring boot·后端
晓蛋13 分钟前
什么是C语言
c语言·跨平台·编程语言·高级语言·低级语言
拖孩39 分钟前
一个人 + AI 做的小程序,今天,正式回本了
前端·后端·微信小程序
进击的丸子1 小时前
代理网络环境下 Linux Pro 在线激活实操指南
javascript·后端·安全
念何架构之路2 小时前
Go reflect包值操作 Value 的读写、可设置性与 interface 布局
开发语言·后端·golang
小林ixn2 小时前
用 FastAPI + SQLAlchemy 从零手搓一个博客系统:一套能落地、能讲清楚的 RESTful 后端
后端·python·fastapi
不识君2 小时前
esl-client 2.0.0 发布:Java 项目接入 FreeSWITCH,从引入到使用
spring boot·后端·开源
牧瀬クリスだ2 小时前
Redis五大数据类型全解析:从入门到实战
java·数据库·redis·后端·缓存
Rain的Java大神之路3 小时前
北漂 13 年,我把 Java 家底钉成了这张知识图谱(全站免费)
java·后端·程序员
Amos_Web3 小时前
通过 Rspack 源码学 Rust(一):`mod`、`pub` 与 `use crate`/`use super`
后端