Программа изменяет значения памяти при использовании calloc () против make () для слайсов - PullRequest
0 голосов
/ 04 ноября 2019

Я пытаюсь создать срез указателей вручную и с помощью C.calloc () для выделения части массива среза. Я могу сделать это успешно, хотя, когда я пытаюсь добавить указатели, которые я выделяю с помощью make (), некоторые значения (на которые указывают указатели) изменяются, по-видимому, случайным образом. С другой стороны, если у меня C.calloc () место для указателей, которые я буду добавлять, значение не изменится. Или, если я выделяю срезы с помощью make (), а добавленные мной указатели выделяются с помощью make (), значения не изменяются.

Я замечаю, что места памяти указателей при использовании C.calloc () vs make () очень разные, но я не понимаю, почему это должно вызывать случайное изменение памяти. Я новичок в Go, поэтому, пожалуйста, прости меня, если я пропускаю некоторые очень простые.

Вот код, который я использую для распределения своих фрагментов вручную:

type caster struct {
    ptr *byte;
    len int64;
    cap int64;
}

var temp caster;
temp.ptr=(*byte)(C.calloc(C.ulong(size),8));
temp.len=int64(size);
temp.cap=int64(size);
newTable.table=*(*[]*entry)(unsafe.Pointer(&temp));

Это работает, если записи, которые яadd распределяются следующим образом:

var temp caster;
var e []entry;
temp.ptr=(*byte)(C.calloc(C.ulong(ninserts),8));
temp.len=int64(ninserts);
temp.cap=int64(ninserts);
e=*(*[]entry)(unsafe.Pointer(&temp));

for i:=0;i<ninserts;i++ {
    e[i].val=hint64(rand.Int63());
}

for i:=0;i<ninserts;i++ {
    ht.insert(&e[i]);
}

, хотя память записей изменяется случайным образом, если они распределяются следующим образом:

var e []entry = make([]entry, ninserts); 

for i:=0;i<ninserts;i++ {
    e[i].val=hint64(rand.Int63());
}

for i:=0;i<ninserts;i++ {
    ht.insert(&e[i]);
}

Если я обычно не строю свои фрагменты следующим образом:

newTable.table = make([]*entry,  size);

1 Ответ

1 голос
/ 04 ноября 2019

Я пытаюсь создать срез указателей вручную и с помощью C.calloc () для выделения части массива среза.

Это явно запрещено.

Цитата из официальной документации cgo :

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

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

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

Выделенное жирным шрифтом моё. Это означает, что вы не должны выделять какие-либо из этих типов с помощью распределителей C.

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

Этот бит вашего собственного кода:

newTable.table=*(*[]*entry)(unsafe.Pointer(&temp));

нарушает правила, но побеждает их исполнение. Вы выделили память C и теперь пытаетесь использовать ее, как если бы это была память Go, в форме среза.

...