为了账号安全,请及时绑定邮箱和手机立即绑定

golang中常量左移后的奇妙问题

golang中常量左移后的奇妙问题

packagemainimport("fmt")const(cimaxuint8=255)funcmain(){variuint8varimaxuint8=255i=imax
查看完整描述

2 回答

?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

以下是一些不够严谨的分析。
参照文档进行研究,Go必然有这么几个特点:
编译型语言,编译器的静态求值是自然的。
必须使用强制类型转换。甚至于对一个uint16变量赋uint8的值都是不允许的。
整数字面值不包含存储空间的大小,可以直接赋给各种uintX(必须不溢出)。
<<运算的结果的类型,以左侧操作数为准。
而从表象上来看:
静态求值的规则是先不顾代码范围,强制求值,之后再塞回目标的存储空间中
对变量左移的时候,会直接考虑存储空间大小,将左移的数据丢弃
对于第1条,如下的代码都会出现同样的错误。对于这两个例子,因为<<左侧都是uint8,所以最终的结果也是uint8,则得出的结果装不下uint8就会报错,哪怕最后准备了uint16的容器也不行:
constc_i8uint8=255
vari16uint16
i16=uint16(c_i8<<4)//constant4080overflowsuint8
i16=uint16(uint8(255)<<4)//constant4080overflowsuint8
对于第2条,如下的代码都可以正确执行:
vari8uint8
vari8_2uint8=255
i8=i8_2<<4
fmt.Printf("%x\n",i8)//f0
i8_2<<=4
fmt.Printf("%x\n",i8_2)//f0
所以并不是说常量不能作为操作数,而是常量左移之后,Go面对溢出的行为并不是截断而是报错。临时对付这个问题,你可能需要先用不定长度的uint类型绕开溢出的限制,再用掩码(&0xFF)运算去约束结果的范围:
constc_i8uint8=255
vari16uint16
i16=uint16(uint(c_i8)<<4&0xFF)
fmt.Printf("%x\n",i16)
更深入的分析期待其他回答者的补充。
                            
查看完整回答
反对 回复 2019-04-08
?
一只斗牛犬

TA贡献1784条经验 获得超2个赞

>>>gobuilda.go
#command-line-arguments
./a.go:18:constant4080overflowsuint8
这错误很明显了啊,溢出了。你那个变量的类型是uint8,能存储的最大值是255。
为什么「变量」没有报溢出的错误呢?因为它是「变量」,编译器在编译时没能确定执行到那里的时候它一定会溢出(说不定你在什么地方改变了它的值所以不会溢出了)。
编译器会保证「常量」不会变化(你不能以正常的方式对它赋值),所以编译的时候它能够把这些地方先计算了,于是它发现你在给一个uint8类型的变量赋值一个超过它的最大值的数。
(大概是因为go太新了吧。像这种C程序,gcc带优化编译完之后只会剩下输出语句,计算都会提前算了。不过对于变量也不会有警告。)
                            
查看完整回答
反对 回复 2019-04-08
  • 2 回答
  • 0 关注
  • 515 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信