У меня есть программа на Go, которая выполняет операции сдвига битов и деления длины строковой константы, но результат не соответствует моим ожиданиям. Вот код:
package main
import "fmt"
const s = "123456789" // len(s) == 9
// len(s) is a constant expression,
// whereas len(s[:]) is not.
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128
func main() {
fmt.Println(a, b) // outputs: 4 0
}
В этой программе a и b вычисляются с использованием аналогичных выражений, включающих сдвиг битов и деление. Однако выходные данные для a и b равны 4 и 0 соответственно, что кажется нелогичным, поскольку обе операции используют одинаковую длину строки и аналогичную арифметику. Может ли кто-нибудь объяснить, почему a и b дают разные результаты?
Я был бы признателен за любую информацию о том, как эти выражения оцениваются по-разному и почему они приводят к разным результатам в Go.
Разница заключается в том, что len(s)
является константой, а len(s[:])
нет, в результате чего первый сдвиг является постоянным сдвигом, а второй — непостоянным сдвигом.
Первый пример — это операция постоянного сдвига, которая будет выполняться в «константном» пространстве, а результат будет преобразован в byte
(так как он умещается в байт).
Второй пример — это непостоянная операция сдвига, поэтому согласно спецификации 1
сначала преобразуется в byte
, затем сдвиг и деление выполняются как значение byte
(результат сдвига не вписывается в byte
, поэтому результат будет 0
), которое, разделенное на 128
, снова будет 0
.
Соответствующий раздел из Спецификация: Операторы:
Правый операнд в выражении сдвига должен иметь целочисленный тип ( Go 1.13) или быть нетипизированной константой, представленной значением типа
uint
. Если левый операнд непостоянного выражения сдвига является нетипизированной константой, он сначала неявно преобразуется в тот тип, который он принял бы, если бы выражение сдвига было заменено только его левым операндом.
См. ссылку по теме: Преобразование оператора сдвига Golang
О том, почему len(s[:])
не является константой, см. Спецификация: выражения среза:
За исключением нетипизированных строк, если срез операнда является строкой или срезом, результатом операции среза является непостоянное значение того же типа, что и операнд. Для нетипизированных строковых операндов результатом является непостоянное значение типа
string
.
Вы можете возразить, что значения индекса также являются константами, нарезка постоянной строки может привести к получению постоянного значения, но эта специализация в настоящее время недоступна, и поскольку она изменит результаты текущих программ (например, в вашем вопросе), это, вероятно, не будет производиться и в будущем.
s — константа, поэтому len(s) известна во время компиляции. Хотя s сама по себе является константой и ее длина может быть определена во время компиляции, s[:] создает новый срез, длина которого не может быть определена во время компиляции, потому что он основан на значениях времени выполнения. Следовательно, len(s[:]) не является постоянным выражением.