Пакет unsafe
Обзор
Пакет unsafe содержит операции, позволяющие обходить проверку типов в программах Go.
Пакеты, импортирующие unsafe, могут быть непереносимыми и не защищены рекомендациями Go 1 по совместимости.
Указатель
Файлы пакета
unsafe.go
func Alignof
func Alignof(x ArbitraryType) uintptr
Alignof принимает выражение x любого типа и возвращает требуемое выравнивание гипотетической переменной v, как если бы она была объявлена с помощью var v = x. Это наибольшее значение m, при котором адрес v всегда делится на m без остатка. Оно совпадает со значением, возвращаемым reflect.TypeOf(x).Align(). В частном случае, если переменная s имеет тип struct, а f — поле этой структуры, Alignof(s.f) вернёт требуемое выравнивание поля этого типа в структуре. Это значение совпадает со значением, возвращаемым reflect.TypeOf(s.f).FieldAlign(). Возвращаемое Alignof значение является константой Go, если тип аргумента не имеет переменного размера. (Определение типов переменного размера см. в описании Sizeof.)
func Offsetof
func Offsetof(x ArbitraryType) uintptr
Offsetof возвращает смещение в структуре поля, представленного выражением x, которое должно иметь вид structValue.field. Иными словами, функция возвращает количество байтов между началом структуры и началом поля. Возвращаемое Offsetof значение является константой Go, если тип аргумента x не имеет переменного размера. (Определение типов переменного размера см. в описании Sizeof.)
func Sizeof
func Sizeof(x ArbitraryType) uintptr
Sizeof принимает выражение x любого типа и возвращает размер в байтах гипотетической переменной v, как если бы она была объявлена с помощью var v = x. Размер не включает память, на которую может ссылаться x. Например, если x — срез, Sizeof возвращает размер дескриптора среза, а не размер памяти, на которую срез ссылается; если x — интерфейс, Sizeof возвращает размер самого значения интерфейсного типа, а не размер значения, хранящегося в интерфейсе. Для структуры размер включает любое заполнение, добавленное для выравнивания полей. Возвращаемое Sizeof значение является константой Go, если тип аргумента x не имеет переменного размера. (Тип имеет переменный размер, если он является параметром типа или массивом либо структурой с элементами переменного размера.)
func String
func String(ptr *byte, len IntegerType) string
String возвращает строковое значение, байты которого начинаются с ptr и имеют длину len.
Аргумент len должен иметь целочисленный тип или быть нетипизированной константой. Константный аргумент len должен быть неотрицательным и представимым значением типа int; если это нетипизированная константа, ей присваивается тип int. Во время выполнения возникает паника, если len отрицателен или если ptr равен nil, а len не равен нулю.
Поскольку строки Go неизменяемы, байты, переданные в String, нельзя изменять, пока существует возвращённое строковое значение.
func StringData
func StringData(str string) *byte
StringData возвращает указатель на байты, лежащие в основе str. Для пустой строки возвращаемое значение не определено и может быть nil.
Поскольку строки Go неизменяемы, байты, возвращённые StringData, нельзя изменять.
type ArbitraryType
ArbitraryType приведён только для целей документации и фактически не является частью пакета unsafe. Он представляет тип произвольного выражения Go.
type ArbitraryType int
func Slice
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
Функция Slice возвращает срез, базовый массив которого начинается с ptr, а длина и ёмкость равны len. Slice(ptr, len) эквивалентна
(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
за исключением частного случая, когда ptr равен nil, а len равен нулю: тогда Slice возвращает nil.
Аргумент len должен иметь целочисленный тип или быть нетипизированной константой. Константный аргумент len должен быть неотрицательным и представимым значением типа int; если это нетипизированная константа, ей присваивается тип int. Во время выполнения возникает паника, если len отрицателен или если ptr равен nil, а len не равен нулю.
func SliceData
func SliceData(slice []ArbitraryType) *ArbitraryType
SliceData возвращает указатель на базовый массив аргумента-среза.
- Если cap(slice) > 0, SliceData возвращает &slice[:1][0].
- Если slice == nil, SliceData возвращает nil.
- В противном случае SliceData возвращает ненулевой указатель на неопределённый адрес памяти.
type IntegerType
IntegerType приведён только для целей документации и фактически не является частью пакета unsafe. Он представляет любой произвольный целочисленный тип.
type IntegerType int
type Pointer
Pointer представляет указатель на произвольный тип. Для типа Pointer доступны четыре специальные операции, недоступные для других типов:
- Значение-указатель любого типа можно преобразовать в Pointer.
- Pointer можно преобразовать в значение-указатель любого типа.
- uintptr можно преобразовать в Pointer.
- Pointer можно преобразовать в uintptr.
Таким образом, Pointer позволяет программе обходить систему типов и читать и записывать произвольную память. Использовать его следует с особой осторожностью.
Следующие шаблоны использования Pointer являются допустимыми. Код, не соответствующий этим шаблонам, скорее всего, уже сейчас некорректен или станет некорректным в будущем. Даже приведённые ниже допустимые шаблоны имеют важные оговорки.
Запуск "go vet" может помочь обнаружить случаи использования Pointer, не соответствующие этим шаблонам, но отсутствие замечаний от "go vet" не гарантирует корректность кода.
(1) Преобразование *T1 в Pointer, а затем в *T2.
Если T2 не больше T1 и их размещение в памяти эквивалентно, это преобразование позволяет интерпретировать данные одного типа как данные другого типа. Примером служит реализация math.Float64bits:
func Float64bits(f float64) uint64 {
return *(*uint64)(unsafe.Pointer(&f))
}
(2) Преобразование Pointer в uintptr (но не обратно в Pointer).
Преобразование Pointer в uintptr возвращает адрес памяти значения, на которое указывает указатель, в виде целого числа. Обычно такое uintptr используют для вывода адреса.
В общем случае преобразование uintptr обратно в Pointer недопустимо.
uintptr — это целое число, а не ссылка. Преобразование Pointer в uintptr создаёт целочисленное значение, не обладающее семантикой указателя. Даже если uintptr содержит адрес некоторого объекта, сборщик мусора не обновит значение uintptr, если объект переместится, и uintptr не предотвратит освобождение объекта.
В оставшихся шаблонах перечислены единственные допустимые преобразования uintptr в Pointer.
(3) Преобразование Pointer в uintptr и обратно с арифметическими операциями.
Если p указывает внутрь выделенного объекта, его можно перемещать в пределах объекта, преобразуя в uintptr, прибавляя смещение и преобразуя обратно в Pointer.
p = unsafe.Pointer(uintptr(p) + offset)
Чаще всего этот шаблон используют для доступа к полям структуры или элементам массива:
// equivalent to f := unsafe.Pointer(&s.f) f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f)) // equivalent to e := unsafe.Pointer(&x[i]) e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))
Таким способом допустимо как прибавлять смещения к указателю, так и вычитать их. Также допустимо использовать &^ для округления указателей, обычно для выравнивания. Во всех случаях результат должен по-прежнему указывать внутрь исходного выделенного объекта.
В отличие от C, перемещать указатель за пределы исходной выделенной области недопустимо:
// INVALID: end points outside allocated space. var s thing end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s)) // INVALID: end points outside allocated space. b := make([]byte, n) end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))
Обратите внимание: оба преобразования должны находиться в одном выражении, а между ними может быть только арифметическая операция:
// INVALID: uintptr cannot be stored in variable // before conversion back to Pointer. u := uintptr(p) p = unsafe.Pointer(u + offset)
Обратите внимание: указатель должен указывать внутрь выделенного объекта, поэтому он не может быть nil.
// INVALID: conversion of nil pointer u := unsafe.Pointer(nil) p := unsafe.Pointer(uintptr(u) + offset)
(4) Преобразование Pointer в uintptr при вызове функций, таких как syscall.Syscall.
Функции Syscall в пакете syscall передают аргументы uintptr непосредственно операционной системе, которая затем может, в зависимости от особенностей вызова, интерпретировать некоторые из них как указатели. Иными словами, реализация системного вызова неявно преобразует некоторые аргументы из uintptr обратно в указатели.
Если аргумент-указатель необходимо преобразовать в uintptr для передачи в качестве аргумента, это преобразование должно находиться непосредственно в выражении вызова:
syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))
Компилятор обрабатывает Pointer, преобразованный в uintptr в списке аргументов вызова функции, реализованной на ассемблере, таким образом, что соответствующий выделенный объект, если он есть, сохраняется и не перемещается до завершения вызова, хотя по одним только типам можно было бы предположить, что объект больше не нужен во время вызова.
Чтобы компилятор распознал этот шаблон, преобразование должно находиться в списке аргументов:
// INVALID: uintptr cannot be stored in variable // before implicit conversion back to Pointer during system call. u := uintptr(unsafe.Pointer(p)) syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))
(5) Преобразование результата reflect.Value.Pointer или reflect.Value.UnsafeAddr из uintptr в Pointer.
Методы типа Value пакета reflect с именами Pointer и UnsafeAddr возвращают тип uintptr, а не unsafe.Pointer, чтобы вызывающие код не мог менять результат на произвольный тип без предварительного импорта "unsafe". Однако из-за этого результат легко становится недействительным, поэтому его необходимо преобразовать в Pointer сразу после вызова, в том же выражении:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
Как и в описанных выше случаях, сохранять результат до преобразования недопустимо:
// INVALID: uintptr cannot be stored in variable // before conversion back to Pointer. u := reflect.ValueOf(new(int)).Pointer() p := (*int)(unsafe.Pointer(u))
(6) Преобразование поля Data типа reflect.SliceHeader или reflect.StringHeader в Pointer или обратно.
Как и в предыдущем случае, структуры данных пакета reflect SliceHeader и StringHeader объявляют поле Data с типом uintptr, чтобы вызывающие код не мог менять результат на произвольный тип без предварительного импорта "unsafe". Однако это означает, что SliceHeader и StringHeader допустимо использовать только для интерпретации содержимого фактического значения среза или строки.
var s string hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) hdr.Len = n
В этом случае hdr.Data — это на самом деле альтернативный способ обратиться к базовому указателю в заголовке строки, а не отдельная переменная типа uintptr.
В общем случае reflect.SliceHeader и reflect.StringHeader следует использовать только как *reflect.SliceHeader и *reflect.StringHeader, указывающие на фактические срезы или строки, но никогда — как обычные структуры. Программа не должна объявлять или выделять переменные этих типов структур.
// INVALID: a directly-declared header will not hold Data as a reference. var hdr reflect.StringHeader hdr.Data = uintptr(unsafe.Pointer(p)) hdr.Len = n s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
type Pointer *ArbitraryType
func Add
func Add(ptr Pointer, len IntegerType) Pointer
Функция Add прибавляет len к ptr и возвращает обновлённый указатель Pointer(uintptr(ptr) + uintptr(len)). Аргумент len должен иметь целочисленный тип или быть нетипизированной константой. Константный аргумент len должен быть представим значением типа int; если это нетипизированная константа, ей присваивается тип int. Правила допустимого использования Pointer также применяются.
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/unsafe/