对于常量表达式和其他表达式来说编译器的评估是不同的 [英] Does go compiler's evaluation differ for constant expression and other expression

查看:228
本文介绍了对于常量表达式和其他表达式来说编译器的评估是不同的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  package main 

import(
fmt
不安全


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:常量2147483648溢出int

上述语句正确。是的,2147483648溢出int(在32位体系结构中)。但转换操作应该会导致负值,即-2147483648。

但是相同的代码起作用,如果我将常量改为变量并获得预期的输出。

 包主
$ b导入(
fmt
不安全


var x int = 1

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


func main(){
fmt.Println(MIN_INT)

}


解决方案

常数表达式和非常数表达式之间的评估存在差异数学常数表示精确的任意精度值,不会溢出。。 p>

类型常量表达式不能溢出;如果结果不能用其类型表示,那就是编译时错误(这可以在编译时检测到)。



同样的东西不适用于非 - 恒定表达式,因为这在编译时不能被检测到(它只能在运行时检测到)。在你的第一个例子中, ONE 是一个类型常量类型为 int 。这个常数表达式:

  ONE<< (unsafe.Sizeof(x)* 8  -  1)

常量 shift expression ,以下情况适用:


如果常量 shift expression 是一个无类型常量,结果是一个整数常量; 否则它是一个与左操作数相同类型的常量,它必须是整数类型<所以移位表达式的结果必须符合 int / code>因为这是一个常量表达式;但由于它没有,所以这是一个编译时错误。



在第二个例子中 ONE 不是常量,它是 int 类型的变量。因此,这里的移位表达式可能会 - 并且会溢出,导致预期的负值。
$ b

注意:



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

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



您可以使用以下解决方案,和最小值 uint int 类型:

<$ p
MinUint = 0
MaxInt = int(MaxUint>> 1)
MinInt = 0
$ const $(
MaxUint = ^ uint(0) -MaxInt - 1


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

输出(在 Go Playground 上试用): p>

  uint:0..4294967295 
int:-2147483648..2147483647

gic背后它在于规格:常量表达式:


一元按位补数运算符^使用的掩码匹配非常数的规则:掩码对无符号常量全为1,对于有符号常量和无类型常量全为1。因此,类型化常量表达式 ^ uint(0)的类型是<$ c

$ c> uint ,并且是 uint 的最大值:它的所有位设置为 1 。鉴于整数使用 2的补码来表示:将它向左移动 1 你会得到max int 的值,min int 0 -MaxInt - 1 -1 c $ c $> value)。

不同行为的推理



为什么没有溢出用于常量表达式和溢出用于非常量表达式?



后者很容易:在大多数其他(编程)语言中都有溢出。所以这种行为与其他语言是一致的,并且它有其优点。



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



Go中的常量比类型变量的值多:它们表示任意精度的精确值。如果你有一个你想分配给一个类型的常量的值,那么允许溢出并分配一个完全不同的值并不真正符合这个词。确定。



继续,这种类型的检查和不允许溢出可以捕获像这样的错误:

 类型Char字节
var c1 Char ='a'// OK
var c2 Char ='世'//编译时错误:常量19990溢出Char

这里会发生什么? c1 char ='a'可以工作,因为'a'符文常量, rune int32 'a'的别名 code>的数值为 97 ,它符合 byte 的有效范围( 0..255 )。



c2 char ='世'导致编译时错误,因为符文'世'的数字值 19990 ,这不符合字节。如果允许溢出,您的代码将编译并将 22 数值('\x16')赋值给 c2 但显然这不是你的意图。通过阻止溢出,这个错误很容易被捕获,并且在编译时。



验证结果:

 var c1 char ='a'
fmt.Printf(%d%q%c\\\
,c1,c1,c1)

// var c2 char ='世'//编译时错误:常量19990溢出char
r:='世'
var c2 Char = Char(r)
fmt.Printf( %d%q%c\\\
,c2,c2,c2)

输出在去游乐场):

  97'a'a 
22'\x16'

要阅读有关常量及其哲学的更多信息,请阅读以下博客文章: Go博客:常量



另外还有一些问题(+答案)涉及到和/或有趣:

Golang:目标int溢出

Go如何对常量执行算术?

查找常量的地址



如何以正确的方式将float64数字更改为uint64?

将10的权力紧凑地写入常量


Why does below code fail to compile?

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)

}

I get an error

main.go:12: constant 2147483648 overflows int

Above statement is correct. Yes, 2147483648 overflows int (In 32 bit architecture). But the shift operation should result in a negative value ie -2147483648.

But the same code works, If I change the constants into variables and I get the expected output.

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)

}

解决方案

There is a difference in evaluation between constant and non-constant expression that arises from constants being precise:

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

Typed constant expressions cannot overflow; if the result cannot be represented by its type, it's a compile-time error (this can be detected at compile-time).

The same thing does not apply to non-constant expressions, as this can't be detected at compile-time (it could only be detected at runtime). Operations on variables can overflow.

In your first example ONE is a typed constant with type int. This constant expression:

ONE << (unsafe.Sizeof(x)*8 - 1)

Is a constant shift expression, the following applies: 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.

So the result of the shift expression must fit into an int because this is a constant expression; but since it doesn't, it's a compile-time error.

In your second example ONE is not a constant, it's a variable of type int. So the shift expression here may –and will– overflow, resulting in the expected negative value.

Notes:

Should you change ONE in the 2nd example to a constant instead of a variable, you'd get the same error (as the expression in the initializer would be a constant expression). Should you change ONE to a variable in the first example, it wouldn't work as variables cannot be used in constant expressions (it must be a constant expression because it initializes a constant).

Constant expressions to find min-max values

You may use the following solution which yields the max and min values of uint and int types:

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)
}

Output (try it on the Go Playground):

uint: 0..4294967295
int: -2147483648..2147483647

The logic behind it lies in the 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.

So the typed constant expression ^uint(0) is of type uint and is the max value of uint: it has all its bits set to 1. Given that integers are represented using 2's complement: shifting this to the left by 1 you'll get the value of max int, from which the min int value is -MaxInt - 1 (-1 due to the 0 value).

Reasoning for the different behavior

Why is there no overflow for constant expressions and overflow for non-constant expressions?

The latter is easy: in most other (programming) languages there is overflow. So this behavior is consistent with other languages and it has its benefits.

The real question is the first: why isn't overflow allowed for constant expressions?

Constants in Go are more than values of typed variables: they represent exact values of arbitrary precision. Staying at the word exact, if you have a value that you want to assign to a typed constant, allowing overflow and assigning a completely different value doesn't really live up to exact.

Going forward, this type checking and disallowing overflow can catch mistakes like this one:

type Char byte
var c1 Char = 'a' // OK
var c2 Char = '世' // Compile-time error: constant 19990 overflows Char

What happens here? c1 Char = 'a' works because 'a' is a rune constant, and rune is alias for int32, and 'a' has numeric value 97 which fits into byte's valid range (which is 0..255).

But c2 Char = '世' results in a compile-time error because the rune '世' has numeric value 19990 which doesn't fit into a byte. If overflow would be allowed, your code would compile and assign 22 numeric value ('\x16') to c2 but obviously this wasn't your intent. By disallowing overflow this mistake is easily caught, and at compile-time.

To verify the results:

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)

Output (try it on the Go Playground):

97 'a' a
22 '\x16' 

To read more about constants and their philosophy, read the blog post: The Go Blog: Constants

And a couple more questions (+answers) that relate and / or are interesting:
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

这篇关于对于常量表达式和其他表达式来说编译器的评估是不同的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆