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