for i, v := range slice 中 v 是副本,修改 v 不影响原切片;须用 users[i].Age = 30 或 &users[i] 修改;取 &v 地址会全部指向最后一次迭代值;map 遍历无序,增删键值行为未定义;字符串 range 返回字节索引,多字节字符需转 []rune 处理。
v 不会改变原切片元素这是最常踩的坑:用 for i, v := range slice 时,v 是元素的副本,不是引用。直接改 v 对原数据毫无影响。
for _, v := range users {
if v.Name == "Alice" {
v.Age = 30 // ✗ 无效:只改了副本
}
}for i, v := range users {
if v.Name == "Alice" {
users[i].Age = 30 // ✓ 改的是原切片第 i 个元素
}
}for i := range users {
u := &users[i]
u.Age = 30 // ✓ 安全且高效
}&v 会导致所有指针指向同一地址因为 v 在每次迭代中是复用的变量(同一内存地址),取它的地址存进 map 或切片,最后所有指针都指向最后一次迭代的值。
s := []int{1, 2, 3}
m := make(map[int]*int)
for k, v := range s {
m[k] = &v // ✗ 全部指向同一个 &v
}
// 最终 m 中所有 *int 都是 3for k, v := range s {
temp := v // ✅ 创建新变量
m[k] = &temp
}for k, v := range s { m[k] = &s[k] } —— 直接取原切片元素地址map 顺序不保证,不能依赖固定顺序range 遍历 map 时起始哈希桶是随机的,每次运行输出顺序都可能不同。这不是 bug,而是 Go 运行时的主动设计,防止开发者误依赖顺序。
keys := make([]string, 0, len(myMap))
for k := range myMap {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Prin
tln(k, myMap[k])
}myMap[k] = v)会直接 panic,必须加锁或改用 sync.Map
range 遍历字符串时,第一个返回值是当前 rune 在字符串中的**字节起始位置**,不是第几个字符。中文、emoji 等多字节字符会让索引“跳变”。
"世" 占 3 字节,若它在字符串第 6 字节开始,下一个字符索引就是 9,不是 7[]rune:s := "Hello 世界"
runes := []rune(s)
fmt.Printf("%c", runes[6]) // ✅ 安全取第 7 个字符("界")range 是最安全的选择;若要修改、插入、按位置访问,[]rune 更直观可靠nil 切片行为不同。range 遍历空切片没问题,但遍历 nil 切片会静默跳过 —— 不报错也不执行循环体,可能掩盖逻辑缺陷。上线前务必确认切片已初始化。