0

0

Go语言range循环赋值机制深度解析:标识符与表达式的异同

霞舞

霞舞

发布时间:2025-11-08 15:00:05

|

161人浏览过

|

来源于php中文网

原创

Go语言range循环赋值机制深度解析:标识符与表达式的异同

本文深入探讨go语言中`range`循环的赋值机制,重点区分了使用标识符(`identifierlist :=`)和表达式(`expressionlist =`)两种方式。通过具体示例,详细阐述了它们在声明新变量和修改现有存储位置上的不同作用,帮助开发者理解并正确运用`range`循环的高级特性。

Go语言的range关键字为我们提供了一种遍历多种数据结构(如数组、切片、字符串、映射和通道)的强大方式。在遍历过程中,range会产生一系列值,并将这些值赋给循环变量。根据Go语言规范,range子句的赋值部分有两种核心形式:

RangeClause = ( ExpressionList "=" | IdentifierList ":=" ) "range" Expression .

这两种形式——使用:=结合标识符列表,或使用=结合表达式列表——虽然都用于赋值,但在语义和用途上存在显著差异。理解这些差异对于编写高效且清晰的Go代码至关重要。

使用标识符声明新变量 (IdentifierList :=)

当我们在range循环中使用IdentifierList :=形式时,我们是在循环的每一次迭代中声明并初始化一个或多个新的局部变量。这些变量仅在循环体内有效,并且每次迭代都会根据range产生的值进行更新。

语法特点:

立即学习go语言免费学习笔记(深入)”;

  • 使用短变量声明操作符:=。
  • 左侧必须是一个或多个标识符(变量名)。
  • 这些标识符在当前作用域内必须是首次声明,或者至少有一个是首次声明(多变量声明时)。

工作原理:

Trickle AI
Trickle AI

多功能零代码AI应用开发平台

下载

在每次迭代开始时,range操作符会生成相应的值(例如,对于切片,是索引和元素值),然后这些值被赋给左侧新声明的标识符。这种方式是range循环最常见、最直观的用法。

示例:

遍历一个整数切片,并打印每个元素的索引。

package main

import "fmt"

func main() {
    // 使用标识符i声明一个新的局部变量,用于接收range产生的索引
    for i := range []int{1, 2, 3} {
        fmt.Println(i) // 输出:0, 1, 2
    }

    // 遍历切片,同时获取索引和值
    for index, value := range []string{"apple", "banana", "cherry"} {
        fmt.Printf("Index: %d, Value: %s\n", index, value)
    }
}

在这个例子中,i、index和value都是在循环体内新声明的变量。它们在每次迭代中被重新赋值,并且在循环结束后,这些变量将超出作用域而无法访问。

使用表达式修改现有存储位置 (ExpressionList =)

与声明新变量不同,当range循环使用ExpressionList =形式时,它会将range产生的值赋给一个或多个已存在的存储位置。这些存储位置可以是变量、结构体字段、数组元素,或者是通过解引用指针得到的内存地址。

语法特点:

立即学习go语言免费学习笔记(深入)”;

  • 使用赋值操作符=。
  • 左侧必须是一个或多个可赋值的表达式。这意味着表达式的结果必须代表一个可写入的内存位置。
  • 表达式可以是:
    • 一个已声明的变量。
    • 一个结构体的字段。
    • 一个数组的元素。
    • 一个解引用的指针(例如*p)。
    • 一个返回可赋值结果的函数调用(例如,返回指针的函数,然后解引用)。

工作原理:

range产生的值不会创建新的变量,而是直接写入到左侧表达式所指向的内存地址。这允许循环直接修改外部变量或通过复杂表达式定位到的数据。

示例:

1. 赋值给解引用的指针:

这个例子展示了如何通过range循环,将遍历得到的值赋给一个由指针指向的外部变量。

package main

import "fmt"

func main() {
    var i = 0 // 外部变量
    p := &i   // p 是指向 i 的指针

    fmt.Printf("Initial i: %d\n", i) // 输出:Initial i: 0

    // 将 range 产生的值赋给 *p,即修改了外部变量 i
    for *p = range []int{1, 2, 3} {
        fmt.Printf("Inside loop, i: %d\n", i)
    }
    fmt.Printf("Final i: %d\n", i) // 输出:Final i: 2 (因为最后一次迭代将2赋给了i)
}

在每次迭代中,range会产生一个索引值(0, 1, 2)。这个索引值被赋给*p,也就是修改了main函数中声明的变量i。因此,每次循环i的值都会更新。

2. 赋值给返回指针的函数结果:

此示例进一步展示了左侧表达式的灵活性,一个函数可以返回一个指针,然后通过解引用该指针来接收range的值。

package main

import "fmt"

var globalVar = 0 // 全局变量

// foo 函数返回 globalVar 的地址
func foo() *int {
    return &globalVar
}

func main() {
    fmt.Printf("Initial globalVar: %d\n", globalVar) // 输出:Initial globalVar: 0

    // 将 range 产生的值赋给 *foo(),即修改了全局变量 globalVar
    for *foo() = range []int{10, 20, 30} {
        fmt.Printf("Inside loop, globalVar: %d\n", globalVar)
    }
    fmt.Printf("Final globalVar: %d\n", globalVar) // 输出:Final globalVar: 20
}

这里,foo()返回globalVar的地址,*foo()则表示globalVar本身。range的索引值(0, 1, 2)被赋给了globalVar。注意,这里range是遍历切片[]int{10, 20, 30},但默认只返回索引。如果需要值,需要写成for _, val := range ...,但本例的原始意图是演示索引赋值给表达式。如果需要赋切片中的值,则需要修改range的源数据类型或使用for _, val := range形式。在提供的例子中,range []int{10, 20, 30}实际上只产生了索引0, 1, 2。因此,globalVar最终的值是2。

总结与注意事项

核心区别

  • IdentifierList :=:用于在循环体内声明新的局部变量,这些变量在每次迭代中被初始化或重新赋值。这是最常见和推荐的用法,尤其是在不需要修改外部状态时。
  • ExpressionList =:用于将range产生的值赋给已存在的存储位置,从而直接修改外部变量或通过表达式定位到的数据。这种方式允许更精细地控制数据的写入,但需要谨慎使用。

何时选择哪种方式:

  • 选择IdentifierList :=
    • 当你只需要在当前循环迭代中使用range产生的值,并且不希望修改循环外部的任何变量时。
    • 这是编写清晰、局部作用域代码的默认选择。
  • 选择ExpressionList =
    • 当你明确需要通过range循环来修改一个或多个外部变量、结构体字段或数组元素时。
    • 例如,你可能正在实现一个填充现有数据结构或更新状态的算法。
    • 需要注意的是,左侧的表达式必须是可赋值的(L-value)。

注意事项:

  1. 可读性与副作用:使用ExpressionList =形式时,代码可能会因为涉及外部状态的修改而变得不那么直观。过度使用或不当使用可能导致难以理解和调试的副作用。
  2. 类型匹配:无论哪种形式,range产生的值的类型必须与左侧变量或表达式的类型兼容。
  3. 多值赋值:range通常会产生两个值(例如,索引和元素值)。如果只需要其中一个,可以使用下划线_来忽略不需要的值。这两种赋值形式都支持多值赋值。

通过深入理解range循环中标识符和表达式赋值的差异,开发者可以更好地利用Go语言的灵活性,编写出既强大又易于维护的代码。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

307

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

182

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

283

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

256

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

122

2025.08.07

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

278

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4.1万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号