Вам нужно инициализировать новую структуру с нулями.GetMem не обнуляет выделенную память, поэтому поля вашей записи изначально содержат случайный мусор.Вам нужно вызвать
FillChar(newItem^, sizeof(TAccessoryItem), 0)
после GetMem, прежде чем использовать запись.
Вот почему: когда вы присваиваете строковое поле вновь выделенного, но неинициализированногоЗа счет записи RTL видит, что поле строки назначения не является нулевым (содержит указатель мусора), и пытается разыменовать строку, чтобы уменьшить ее счетчик ссылок, прежде чем присваивать этому полю новое значение строки.Это необходимо при каждом присваивании строковому полю или переменной, чтобы предыдущее строковое значение было освобождено, если ничто другое не использует его, прежде чем новое значение будет присвоено строковому полю или переменной.
Поскольку указательфигня, вы получаете нарушение прав доступа ... если вам повезет.Возможно, что случайный указатель мусора может совпадать со значением, указывающим на выделенный диапазон адресов в вашем процессе.Если бы это произошло, вы бы не получили AV в точке назначения, но, скорее всего, получили бы гораздо худший и гораздо более загадочный сбой позже при выполнении программы, потому что это присвоение строки неинициализированной переменной изменило память где-то еще в вашем процессенеуместно.
Всякий раз, когда вы имеете дело с указателями памяти напрямую и тип, который вы выделяете, содержит управляемые поля компилятора, вы должны быть очень осторожны при инициализации и расположении типа, чтобы управляемые компилятором поля инициализировались иутилизируется правильно.
Лучший способ разместить записи в Delphi - это использовать функцию New ():
New(newItem);
Компилятор выведет размер выделения из типа указателя (sizeof, на который указывает тип указателя), выделите память и инициализируйте все поля, подходящие для вас.
Соответствующим освобождением является функция Dispose ():
Dispose(newItem);
Это сделаетуверен, что все управляемые компилятором файлыПоля записи удаляются правильно, в дополнение к освобождению памяти, используемой самой записью.
Если вы просто FreeMem (newItem), вы потеряете память, потому что память, занятая строками и другими управляемыми полями компилятора в этой записи, не будет освобождена.
Управляемые типы компилятора включают длинные строки(«String», а не «string [10]»), широкие строки, варианты, интерфейсы и, возможно, то, что я забыл.
Один из способов думать таков: GetMem / FreeMem просто выделяет и выпускаетблоки памяти.Они ничего не знают о том, как этот блок будет использоваться.New и Dispose, тем не менее, «осведомлены» о типе, для которого вы выделяете или освобождаете память, поэтому они сделают любую дополнительную работу, чтобы убедиться, что вся внутренняя домашняя работа позаботится автоматически.
В общем, лучше избегать GetMem / FreeMem, если все, что вам действительно нужно, это необработанный блок памяти без связанной с ним семантики типов.