#go
#Вперед
Вопрос:
Согласно документации (https://golang.org/pkg/unsafe/#Sizeof ) unsafe.Sizeof
возвращает размер заданного выражения в байтах. Размер любого заданного выражения в идеале может быть обозначен символом uint32
или uint64
. Тогда почему Golang возвращает a uintptr
вместо этого? Разве это не сбивает с толку? uintptr
Предполагается, что A содержит указатель на некоторое значение данных, но в данном случае это на самом деле не указатель, а просто число, верно?
Комментарии:
1.
unitptr
это целочисленный тип, который достаточно велик, чтобы содержать любой указатель. Из этого следует, чтоuintptr
он достаточно велик, чтобы вместить размер любого значения.uint32
На некоторых архитектурах A недостаточно велик. Auint64
больше, чем требуется на некоторых архитектурах.2. Предполагается, что A
uintptr
не содержит указатель, это просто значение без знака размером с указатель (за исключением небезопасных вариантов использования, у вас никогда не должно быть фактического указателя, сохраненного как uintptr). Это единственный архитектурный числовой тип без знака.3. Кроме того, поскольку
uintptr
он может быть преобразован вunsafe.Pointer
, его можно использовать для (небезопасной!) арифметики указателей, для которойSizeof
могут использоваться такие вещи, как.4.
uintptr is an integer type that is large enough to hold the bit pattern of any pointer.
— официальное описание типа uintptr.5. Это не просто число . (Ну, это действительно так, но тогда и указатель тоже.) Это разница между двумя указателями (на начало и конец гипотетической переменной, которая будет создана выражением). Мы могли бы добавить новый
intptrdiff
тип, но это попахивает C иuintptr
довольно хорошо подходит.
Ответ №1:
В комментариях есть много хороших ответов, которые сводятся к «потому что это достаточно большое, но не слишком большое». Я думаю, однако, было бы полезно рассмотреть это с исторической точки зрения, уделяя особое внимание тому, как все это появилось на языке программирования C.
В очень старом (до стандартного) C, если вы вернетесь достаточно далеко назад во времени, не было даже явного unsigned
целочисленного типа. PDP-11 имел:
char
, который был 8 бит и подписан;int
, который был 16 бит и подписан; и- указатели, которые были 16 бит и без знака.
То есть:
int i;
int *u;
так вы сделали два целых числа, i
подписанных и u
неподписанных. Установка i
значения 32767 (0x7fff) и последующее его увеличение дали вам -32768 (0x8000), которое постепенно увеличивалось до -1 (0xffff), а затем до нуля. Установка u
значения 32767 и последующее его увеличение дали вам 32768, который постепенно увеличился до 65535, а затем перевернулся до нуля.
Отсутствие различия между целыми числами и указателями означало, что драйверы устройств могли читать:
struct {
int csr;
int blk;
int bar;
int bcr;
};
0177440->bcr = count;
0177440->blk = block;
0177440->bar = addr;
0177440->csr = READ | GO;
возможно, так кто-то сказал устройству прочитать несколько байтов или блоков.
(Это также причина, по которой имена членов структуры, такие как st_ino
in struct stat
, были с таким префиксом: st_ino
просто означало «некоторое целочисленное смещение», и вы могли использовать st_ino
член с любым указателем или даже с обычной переменной. Префикс означал, что вы можете #include
использовать несколько заголовков без столкновения имен членов их структуры.)
Все это оказалось несостоятельным, когда C был создан для работы на 32-разрядных и других машинах. C увеличил unsigned
целочисленный тип, вместо того, чтобы использовать указатели в качестве целых чисел без знака, а компилятор PCC Стива Джонсона превратился unsigned
в модификатор, который можно применять к char
and short
, а также as int
. Было проведено много экспериментов. В конце концов, в 1989 году C был впервые стандартизирован с большинством синтаксиса и семантики, которые мы имеем сейчас (хотя новые стандарты добавили новые типы, множество функций и так далее).
Некоторые из первых пионеров C были вовлечены в создание Go, при особом влиянии Кена Томпсона. На странице Википедии есть цитата, которая здесь уместна:
Когда мы трое [Томпсон, Роб Пайк и Роберт Гриземер] начали, это было чистое исследование. Мы втроем собрались и решили, что ненавидим C . [смех] … [Возвращаясь к Go] мы начали с идеи, что все трое из нас должны быть ознакомлены с каждой функцией языка, чтобы ни по какой причине в язык не был добавлен посторонний мусор.
Как мы видим с первых дней C, указатель как целое число является подходящим типом без знака, который может не только содержать любой указатель, но, если он обрабатывается как беззнаковый, может также содержать любой размер объекта. Указатель в виде целого числа, конечно, не может использоваться непосредственно как указатель, а с системой GC и параллелизмом нам нужен сам язык, чтобы иметь указатели. Но нам также нужно иметь возможность писать поддержку среды выполнения для языка,1 для чего нам нужны целочисленные указатели, которые также охватывают все наши потребности в размерах объектов. Итак, один тип, встроенный в компилятор, удовлетворяет всем требованиям. Это настолько просто, насколько это возможно, но не проще.
1 Я говорю «мы», как будто я имею к этому какое-то отношение. Это просто очевидно, как только вы внедрили несколько систем времени выполнения.