Неожиданный результат при сдвиге битов с использованием len в строке и подсрезе строки

У меня есть программа на 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 дают разные результаты?

  • Что в этом контексте делает деление на 128 и сдвиг битов?
  • Почему len(s[:]) не считается постоянным выражением и как это влияет на вычисление?

Я был бы признателен за любую информацию о том, как эти выражения оцениваются по-разному и почему они приводят к разным результатам в Go.

s — константа, поэтому len(s) известна во время компиляции. Хотя s сама по себе является константой и ее длина может быть определена во время компиляции, s[:] создает новый срез, длина которого не может быть определена во время компиляции, потому что он основан на значениях времени выполнения. Следовательно, len(s[:]) не является постоянным выражением.

Sarath Sadasivan Pillai 17.04.2024 09:35
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
6
1
262
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Разница заключается в том, что len(s) является константой, а len(s[:]) нет, в результате чего первый сдвиг является постоянным сдвигом, а второй — непостоянным сдвигом.

Первый пример — это операция постоянного сдвига, которая будет выполняться в «константном» пространстве, а результат будет преобразован в byte (так как он умещается в байт).

Второй пример — это непостоянная операция сдвига, поэтому согласно спецификации 1 сначала преобразуется в byte, затем сдвиг и деление выполняются как значение byte (результат сдвига не вписывается в byte, поэтому результат будет 0), которое, разделенное на 128, снова будет 0.

Соответствующий раздел из Спецификация: Операторы:

Правый операнд в выражении сдвига должен иметь целочисленный тип ( Go 1.13) или быть нетипизированной константой, представленной значением типа uint. Если левый операнд непостоянного выражения сдвига является нетипизированной константой, он сначала неявно преобразуется в тот тип, который он принял бы, если бы выражение сдвига было заменено только его левым операндом.

См. ссылку по теме: Преобразование оператора сдвига Golang

О том, почему len(s[:]) не является константой, см. Спецификация: выражения среза:

За исключением нетипизированных строк, если срез операнда является строкой или срезом, результатом операции среза является непостоянное значение того же типа, что и операнд. Для нетипизированных строковых операндов результатом является непостоянное значение типа string.

Вы можете возразить, что значения индекса также являются константами, нарезка постоянной строки может привести к получению постоянного значения, но эта специализация в настоящее время недоступна, и поскольку она изменит результаты текущих программ (например, в вашем вопросе), это, вероятно, не будет производиться и в будущем.

Другие вопросы по теме