понедельник, 15 июля 2019 г.

Команда cgo: передача указателей

Go - это язык со сборщиком мусора, и сборщик мусора должен знать расположение каждого указателя на память Go. Из-за этого существуют ограничения на передачу указателей между Go и C.

В этом посте термин указатель Go означает указатель на память, выделенную Go (например, с помощью оператора & или вызвав предопределенную функцию new), а термин C указатель означает указатель на память, выделенную C (например, путем вызова C.malloc). Является ли указатель указателем Go или указателем C, в любом случае это динамическое свойство, определяемое тем, как была выделена память; это не имеет ничего общего с типом указателя.

Обратите внимание, что значения некоторых типов Go, кроме нулевого значения типа, всегда включают указатели Go. Это верно для типов строки, среза, интерфейса, канала, карты и функций. Тип указателя может содержать указатель Go или указатель C. Типы массивов и структур могут включать или не включать указатели Go в зависимости от типов элементов. Все обсуждения ниже по поводу указателей Go применяются не только к типам указателей, но и к другим типам, которые включают указатели Go.

Код Go может передавать указатель Go в C, если память Go, на которую он указывает, не содержит указателей Go. Код C должен сохранять это свойство: он не должен хранить указатели Go в памяти Go, даже временно. При передаче указателя на поле в структуре рассматриваемая память Go - это память, занятая полем, а не вся структура. При передаче указателя на элемент в массиве или срезе рассматриваемая память Go представляет собой весь массив или весь базовый массив среза.

Код C может не сохранять копию указателя Go после возврата вызова. Это включает в себя тип _GoString_, который, как отмечено выше, включает в себя указатель Go; Значения _GoString_ не могут быть сохранены кодом C.

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

Код Go не может хранить указатель Go в памяти C. Код C может хранить указатели Go в памяти C, в соответствии с приведенным выше правилом: он должен прекратить хранить указатель Go, когда функция C вернется.

Эти правила проверяются динамически во время выполнения. Проверка контролируется параметром cgocheck переменной среды GODEBUG. Значением по умолчанию является GODEBUG=cgocheck=1, которое реализует достаточно дешевые динамические проверки. Эти проверки могут быть полностью отключены с помощью GODEBUG=cgocheck=0. Полная проверка обработки указателя, за определенную плату во время выполнения, доступна через GODEBUG=cgocheck=2.

Можно победить это применение с помощью unsafe пакета, и, конечно, ничто не мешает коду C делать все, что ему нравится. Однако программы, нарушающие эти правила, могут потерпеть неудачу неожидаемым и непредсказуемым образом.

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


Читайте также:


Комментариев нет:

Отправить комментарий