Is it safe to swap two integers by `a, b = b, a` in golang?

huangapple go评论60阅读模式
英文:

Is it safe to swap two integers by `a, b = b, a` in golang?

问题

package main

import "fmt"

func main() {
    a := 1
    b := 2

    fmt.Printf("交换前: %v %v\n", a, b)
    a, b = b, a
    fmt.Printf("交换后: %v %v\n", a, b)
}

输出结果为:

交换前: 1 2
交换后: 2 1

看起来很好。我只是想知道:

在 Go 语言中,使用 a, b = b, a 来交换两个整数是否安全?

英文:
package main

import "fmt"

func main() {
	a := 1
	b := 2

	fmt.Printf("Before Swap: %v %v\n", a, b)
	a, b = b, a
	fmt.Printf(" After Swap: %v %v\n", a, b)
}

The output is:

Before Swap: 1 2  
 After Swap: 2 1

It looks good. I just wonder:

Is it safe to swap two integers by a, b = b, a in golang?

答案1

得分: 2

是的,使用以下代码可以安全地交换两个变量的值:

a, b = b, a

赋值操作分为两个步骤:

  • 首先计算赋值语句右侧的表达式。
  • 然后按从左到右的顺序将计算得到的值赋给左侧的变量。

换句话说,a, b = b, a 大致上会被重写为:

tmpB := b
tmpA := a
a = tmpB
b = tmpA

这就是为什么这种赋值是安全的。


实际上,编译器足够智能,可以避免使用第二个临时变量,所以 a, b = b, a 会被重写为:

tmpA := a
a = b
b = tmpA
$ cat x.go 
package p

func f(a, b int) {
	a, b = b, a
}
$ go tool compile -W x.go

before walk f
.   AS2 tc(1) # x.go:4:7
.   AS2-Lhs
.   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   AS2-Rhs
.   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
after walk f
.   BLOCK # x.go:4:7
.   BLOCK-List
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p..autotmp_2 esc(N) Class:PAUTO Offset:0 AutoTemp OnStack Used int tc(1) # x.go:4:7
.   .   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   .   NAME-p..autotmp_2 esc(N) Class:PAUTO Offset:0 AutoTemp OnStack Used int tc(1) # x.go:4:7
英文:

Yes, it's safe to swap two variables using:

a, b = b, a

The assignment happens in two steps:

  • The right hand side of the assignment is evaluated first.
  • Then the evaluated values will be assigned to the left hand side in left-to-right order.

In other words, a, b = b, a will be rewritten roughly to:

tmpB := b
tmpA := a
a = tmpB
b = tmpA

That's why the assignment is safe.


In fact, the compiler is smart enough to avoid a second temporary variable, so a, b = b, a will be rewritten as:

tmpA := a
a = b
b = tmpA
$ cat x.go 
package p

func f(a, b int) {
	a, b = b, a
}
$ go tool compile -W x.go

before walk f
.   AS2 tc(1) # x.go:4:7
.   AS2-Lhs
.   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   AS2-Rhs
.   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
after walk f
.   BLOCK # x.go:4:7
.   BLOCK-List
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p..autotmp_2 esc(N) Class:PAUTO Offset:0 AutoTemp OnStack Used int tc(1) # x.go:4:7
.   .   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p.a esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:8
.   .   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   AS tc(1) # x.go:4:7
.   .   .   NAME-p.b esc(no) Class:PPARAM Offset:0 OnStack Used int tc(1) # x.go:3:11
.   .   .   NAME-p..autotmp_2 esc(N) Class:PAUTO Offset:0 AutoTemp OnStack Used int tc(1) # x.go:4:7

答案2

得分: 0

是的,当你有相同的变量类型时,你可以交换它们。

i := []int{1, 2, 3, 4}
i[0], i[1], i[2], i[3] = i[3], i[2], i[1], i[0]
// 现在,i 的值为:[]int{4, 3, 2, 1}
// 另外,你也可以使用 range 遍历切片并进行交换操作

根据文档中的说明:

在赋值语句中,每个值必须可以赋值给其被赋值的操作数的类型,以下是特殊情况:

  1. 任何类型的值都可以赋值给空白标识符。
  2. 如果将无类型常量赋值给接口类型的变量或空白标识符,则该常量首先会被隐式转换为其默认类型
  3. 如果将无类型布尔值赋值给接口类型的变量或空白标识符,则它首先会被隐式转换为 bool 类型。
英文:

Yes, it is. When you have same variable type, you can swipe them.

i := []int{1, 2, 3, 4}
i[0], i[1], i[2], i[3] = i[3], i[2], i[1], i[0]
// now, i is: []int{4, 3, 2, 1}
// btw, you could also range over slice and swipe them

From the docs

In assignments, each value must be assignable to the type of the operand to which it is assigned, with the following special cases:

  1. Any typed value may be assigned to the blank identifier.
  2. If an untyped constant is assigned to a variable of interface type or the blank identifier, the constant is first implicitly converted to its default type.
  3. If an untyped boolean value is assigned to a variable of interface type or the blank identifier, it is first implicitly converted to type bool.

huangapple
  • 本文由 发表于 2023年6月4日 14:43:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76399227.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定