почему сравнение массивов имеет другой результат - PullRequest
0 голосов
/ 20 сентября 2018

Я не знаю, почему происходит следующее, и я не могу найти относительный исходный код.Кто-нибудь может мне объяснить?

var s, ss struct{} // two empty structs
arr1 := [6]*struct{}{&s} // array with empty struct pointer
arr2 := [6]*struct{}{&ss} // array with empty struct pointer
fmt.Println(&s == &ss, arr1 == arr2)  // false, true

var l, ll struct{A int}{}
arr3 := [6]*struct{A int}{&l} // array with empty struct pointer
arr4 := [6]*struct{A int}{&ll} // array with empty struct pointer
fmt.Println(&l == &ll, arr3 == arr4)  // false, false

1 Ответ

0 голосов
/ 20 сентября 2018

Spec: Операторы сравнения:

Значения указателя сопоставимы.Два значения указателя равны, если они указывают на одну и ту же переменную или если оба имеют значение nil. Указатели на различные переменные нулевого размера могут совпадать или не совпадать.

А также Спецификация: Размер и гарантии выравнивания:

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

Размер переменных s и ss равен нулю, поэтому &s и &ss являются указателями на различные переменные нулевого размера, поэтому спецификация ничего не гарантирует об их равенстве.Это означает, что &s == &ss может оценивать как true или false, вы не можете рассчитывать на результат, и это было бы ошибкой.

Тем не менее,странно, что в течение одного времени выполнения приложения, когда они равны, а когда нет.Урок состоит в том, чтобы никогда не полагаться на него.

Другое поведение можно объяснить, взглянув на анализ побега.

Давайте упростим ваше приложение до следующего:

var s, ss struct{}                   // two empty structs
arr1 := [6]*struct{}{&s}             // array with empty struct pointer
arr2 := [6]*struct{}{&ss}            // array with empty struct pointer
fmt.Println(&s == &ss, arr1 == arr2) // false, true

Запуск анализа escape с помощью go run -gcflags '-m' play.go дает:

./play.go:13:17: &s == &ss escapes to heap
./play.go:13:30: arr1 == arr2 escapes to heap
./play.go:11:23: main &s does not escape
./play.go:12:23: main &ss does not escape
./play.go:13:14: main &s does not escape
./play.go:13:20: main &ss does not escape
./play.go:13:13: main ... argument does not escape
false true

&s и &ss не экранируют (так как они не передаются в fmt.Println(), только результат &s == &ss).

Если мы добавим одну строку в вышеупомянутое упрощенное приложение:

var s, ss struct{}                   // two empty structs
arr1 := [6]*struct{}{&s}             // array with empty struct pointer
arr2 := [6]*struct{}{&ss}            // array with empty struct pointer
fmt.Println(&s == &ss, arr1 == arr2) // true, true

fmt.Printf("%p %p\n", &s, &ss) // true, true

Запуск анализа выхода теперь дает:

./play.go:13:17: &s == &ss escapes to heap
./play.go:13:30: arr1 == arr2 escapes to heap
./play.go:15:24: &s escapes to heap
./play.go:15:24: &s escapes to heap
./play.go:10:6: moved to heap: s
./play.go:15:28: &ss escapes to heap
./play.go:15:28: &ss escapes to heap
./play.go:10:9: moved to heap: ss
./play.go:11:23: main &s does not escape
./play.go:12:23: main &ss does not escape
./play.go:13:14: main &s does not escape
./play.go:13:20: main &ss does not escape
./play.go:13:13: main ... argument does not escape
./play.go:15:12: main ... argument does not escape
true true

Поведение изменилось: теперь мы видим true true output (попробуйте на Go Playground ).

Причина изменения поведения в том, что &s и &ss уходят в кучу: они напрямую передаются на fmt.Println(), поэтому компилятор изменил, как (где) они хранятся, и вместе с этим изменился и их адрес.

См. связанный / возможный дубликат: Адрес Голанга фрагментов пустых структур

...