前言
相信用过Range的朋友们都知道,Go语言中的range关键字使用起来非常的方便,它允许你遍历某个slice或者map,并通过两个参数(index
和value
),分别获取到slice或者map中某个元素所在的index
以及其值。
比如像这样的用法:
for index, value := range mySlice { fmt.Println("index: " + index) fmt.Println("value: " + value) }
上面的例子足够清晰的描述了range的用法,实际上在使用range关键字的时候,还有一些需要特别注意的地方,有一些新手很容易入的”坑”。
为了说明这些”坑”,我们可以从下面这个稍复杂的例子说起:
type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) for i, value := range list { list2[i] = &value } fmt.Println(list[0], list[1], list[2]) fmt.Println(list2[0], list2[1], list2[2]) }
在这个例子中,我们干了下面的一些事情:
1、定义了一个叫做Foo的结构,里面有一个叫bar的field。随后,我们创建了一个基于Foo结构体的slice,名字叫list
2、我们还创建了一个基于Foo结构体指针类型的slice,叫做list2
3、在一个for
循环中,我们试图遍历list中的每一个元素,获取其指针地址,并赋值到list2中index与之对应的位置。
4、最后,分别输出list与list2中的每个元素
从代码来看,理所当然,我们期望得到的结果应该是这样:
{A} {B} {C} &{A} &{B} &{C}
但是结果却出乎意料,程序的输出是这样的:
{A} {B} {C} &{C} &{C} &{C}
从结果来看,仿佛list2中的三个元素,都指向了list中的最后一个元素。这是为什么呢?问题就出在上面那一段for…range
循环中。
在Go的for…range
循环中,Go始终使用值拷贝的方式代替被遍历的元素本身,简单来说,就是for…range
中那个value
,是一个值拷贝,而不是元素本身。这样一来,当我们期望用&获取元素的指针地址时,实际上只是取到了value
这个临时变量的指针地址,而非list
中真正被遍历到的某个元素的指针地址。而在整个for…range
循环中,value
这个临时变量会被重复使用,所以,在上面的例子中,list2被填充了三个相同的指针地址,并且这三个地址都指向value
,而在最后一次循环中,value
被赋与了{c}
的指针地址。因此,list2输出的时候显示出了三个&{c}
。
同样的,下面的写法,跟for…range
的例子如出一辙:
var value Foo for var i := 0; i < len(list); i++ { value = list[i] list2[i] = &value }
如果我们输出list2的三个元素,结果同样是: &{C} &{C} &{C}
那么,怎样才是正确的写法呢?我们应该用index
来访问for…range
中真实的元素,并获取其指针地址:
for i, _ := range list { list2[i] = &list[i] }
这样,输出list2中的元素,就能得到我们想要的结果(&{A} &{B} &{C})
了。
实验代码如下:
package main import "fmt" type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) //错误的例子 for i, value := range list { list2[i] = &value } //正确的例子 //for i, _ := range list { // list2[i] = &list[i] //} fmt.Println(list[0], list[1], list[2]) fmt.Println(list2[0], list2[1], list2[2]) }
了解了range的正确使用姿势,那么我们下面这个例子也能迎刃而解了:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for _, e := range array { e.field = "foo" } for _, e := range array { fmt.Println(e.field) fmt.Println("--") } }
平常写代码最常见的场景,就是我们需要在一个循环中修改被遍历元素的值。比如上面这个例子,我们希望能使用for…range
循环,一次性将array中每个元素的field设置为”foo”。同样,因为range值拷贝的缘故,上面的程序什么都不会输出……
而正确的做法是:
for i, _ := range array { array[i].field = "foo" }
通过index访问每个元素,并修改其field,这样,就能输出一堆”foo”了……
实验代码如下:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for i, _ := range array { array[i].field = "foo" } for _, e := range array { fmt.Println(e.field) } }
总结
以上就是关于Go语言中Range关键字的全部内容,这篇文章介绍的还是很详细的,相信本文会对大家学习Go语言具有一定的参考价值,如果有疑问大家可以留言交流,小编会尽快给大家回复的,也请大家继续支持。
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]