gpt4 book ai didi

go - 对于常量表达式和其他表达式,Go编译器的评估是否有所不同

转载 作者:IT王子 更新时间:2023-10-29 00:52:19 25 4
gpt4 key购买 nike

为什么下面的代码无法编译?

package main

import (
"fmt"
"unsafe"
)

var x int = 1

const (
ONE int = 1
MIN_INT int = ONE << (unsafe.Sizeof(x)*8 - 1)
)

func main() {
fmt.Println(MIN_INT)

}

我得到一个错误

main.go:12: constant 2147483648 overflows int



以上说法是正确的。是的,2147483648溢出int(在32位体系结构中)。但是移位操作将导致负值,即-2147483648。

但是相同的代码也可以工作,如果我将常量更改为变量并获得了预期的输出。
package main

import (
"fmt"
"unsafe"
)

var x int = 1

var (
ONE int = 1
MIN_INT int = ONE << (unsafe.Sizeof(x)*8 - 1)
)

func main() {
fmt.Println(MIN_INT)

}

最佳答案

在常量和非常量表达式之间的评估存在差异,这是由于精确的常量引起的:

Numeric constants represent exact values of arbitrary precision and do not overflow.



类型化的常量表达式不会溢出;如果结果不能用其类型表示,则为编译时错误(可以在编译时检测到)。

相同的事情不适用于非常量表达式,因为在编译时无法检测到(只能在运行时检测到)。对变量的操作可能会溢出。

在第一个示例中, ONE是类型为 int的类型化常量。这个常量表达式:
ONE << (unsafe.Sizeof(x)*8 - 1)

是一个常量 shift expression,适用以下条件: Spec: Constant expressions:

If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.



因此,shift表达式的结果必须适合 int,因为这是一个常量表达式。但是由于没有,这是一个编译时错误。

在第二个示例中, ONE不是常量,而是 int类型的变量。因此,此处的移位表达式可能(并且将)溢出,从而导致期望的负值。

注意:

如果将第二个示例中的 ONE更改为常量而不是变量,则会得到相同的错误(因为初始化器中的表达式将是常量表达式)。如果在第一个示例中将 ONE更改为变量,则该变量将不起作用,因为变量不能在常量表达式中使用(它必须是常量表达式,因为它会初始化常量)。

用于查找最小值-最大值的常量表达式

您可以使用以下解决方案来产生 uintint类型的最大值和最小值:
const (
MaxUint = ^uint(0)
MinUint = 0
MaxInt = int(MaxUint >> 1)
MinInt = -MaxInt - 1
)

func main() {
fmt.Printf("uint: %d..%d\n", MinUint, MaxUint)
fmt.Printf("int: %d..%d\n", MinInt, MaxInt)
}

输出(在 Go Playground上尝试):
uint: 0..4294967295
int: -2147483648..2147483647

其背后的逻辑在于 Spec: Constant expressions:

The mask used by the unary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.



因此,类型化常量表达式 ^uint(0)的类型为 uint,并且是 uint的最大值:它的所有位都设置为 1。假定使用 2's complement表示整数:将其向左移动 1,您将获得max int的值,其中,最小 int的值为 -MaxInt - 1(由于 -1的值,因此为 0)。

推理不同的行为

为什么常量表达式没有 overflow,非常量表达式为什么没有溢出?

后者很简单:在大多数其他(编程)语言中,都有溢出。因此,这种行为与其他语言一致,并且具有其优势。

真正的问题是第一个:为什么常量表达式不允许溢出?

Go中的常量不仅仅是类型化变量的值:它们表示任意精度的精确值。坚持精确一词,如果您有一个要分配给类型常量的值,则允许溢出并分配一个完全不同的值并不能真正实现精确。

展望 future ,这种类型的检查和禁止溢出可能会捕获如下错误:
type Char byte
var c1 Char = 'a' // OK
var c2 Char = '世' // Compile-time error: constant 19990 overflows Char

发生什么事了? c1 Char = 'a'之所以有效,是因为 'a'rune常量,并且 runeint32的别名,并且 'a'具有数值 97,该值适合 byte的有效范围(即 0..255)。

但是 c2 Char = '世'导致编译时错误,因为 rune '世'的数字值 19990不适合 byte。如果允许溢出,您的代码将编译并将 22数值( '\x16')分配给 c2,但显然这不是您的意图。通过禁止溢出,很容易在编译时发现此错误。

要验证结果:
var c1 Char = 'a'
fmt.Printf("%d %q %c\n", c1, c1, c1)

// var c2 Char = '世' // Compile-time error: constant 19990 overflows Char
r := '世'
var c2 Char = Char(r)
fmt.Printf("%d %q %c\n", c2, c2, c2)

输出(在 Go Playground上尝试):
97 'a' a
22 '\x16'

要了解有关常量及其原理的更多信息,请阅读博客文章: The Go Blog: Constants

还有另外两个与(或)有趣的问题(+答案):
Golang: on-purpose int overflow
How does Go perform arithmetic on constants?
Find address of constant in go
Why do these two float64s have different values?
How to change a float64 number to uint64 in a right way?
Writing powers of 10 as constants compactly

关于go - 对于常量表达式和其他表达式,Go编译器的评估是否有所不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39444852/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com