text/scanner/scanner.go
中的Go扫描程序包使用技巧来查找空格:
const GoWhitespace = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '
然后:
// skip white space
for s.Whitespace&(1<<uint(ch)) != 0 {
ch = s.next()
}
由于字符值向左移动超过31,是否会出现不唯一的情况?我的意思是,当一些char与tab modulo 32相同时,它会被识别为空格?
答案 0 :(得分:2)
如上所述in the spec,<<
是一个轮班操作:
移位运算符将左操作数移位右操作数指定的移位计数。如果左操作数是有符号整数,则它们实现算术移位;如果是无符号整数,则它们实现逻辑移位。班次计数没有上限。对于移位计数n,移位的行为就好像左操作数被移位n次1。结果,x <&lt;&lt; 1与x * 2和x>&gt;相同。 1与x / 2相同但截断为负无穷大。
对于ch
的大值,1<<uint(ch)
将cause an overflow:
对于无符号整数值,操作+, - ,*和&lt;&lt;计算模2 n ,其中n是无符号整数类型的位宽。松散地说,这些无符号整数运算在溢出时丢弃高位,程序可能依赖于“环绕”。
对于有符号整数,操作+, - ,*和&lt;&lt;可能合法地溢出并且结果值存在并且由有符号整数表示,操作及其操作数确定性地定义。溢出不会引发异常。在假设不发生溢出的情况下,编译器可能不会优化代码。例如,它可能不假设x < x + 1始终为真。
因此,使用按位旋转运算符(您似乎在描述的内容)实现<<
会违反规范。对于大于1<<uint(ch)
类型大小的ch
值,int
将评估为零,因此不会导致任何误报。
答案 1 :(得分:0)
完全回答:
Spec明确说明对于无符号运算,我们将高位掩盖掉,这样低位实际上是“环绕”。
它起作用的原因是:
Scanner.Whitespace
实际上是uint64
,因此GoWhitespace
的值完全符合s.Whitespace&(1<<uint(ch))
可以具有任意大的中间值并且将环绕。因此,如果说char是“a”(96),我们有1 << 96
溢出,因此模数大小为64位int为0。