Ой, этот код (к сожалению) очень плохой. Ваша функция возвращает указатель на массив ByteArr, но, к сожалению, этот массив выходит из области видимости, когда функция существует: вы по сути возвращаете Invalid Pointer! Даже если ошибка не появляется сразу же, у вас есть скрытое нарушение прав доступа.
Более длинное объяснение
A Pointer
- опасная структура: она не содержит данных, она просто говорит, где эти данные существуют. Ваш пример нетипизированного Pointer
является самым сложным видом указателя, он говорит ничего о данных, которые существуют по данному адресу. Он может указывать на некоторые байты, которые вы читаете из потока, может указывать на строку или даже изображение какого-то рода. Вы даже не можете знать количество данных по данному адресу.
Концепция Pointer тесно связана с концепцией выделения памяти. Мы используем много разных техник для выделения памяти, используя локальные переменные, глобальные переменные, объекты, динамические массивы и т. Д. В вашей функции-примере вы используете динамический массив array of Byte
. Компилятор очень хорошо защищает вас от внутренней памяти выделения и перераспределения памяти, вы можете просто использовать SetLength()
, чтобы сказать, насколько большим должен быть массив. Все работает очень хорошо, потому что динамический массив представляет собой управляемую структуру данных в Delphi: компилятор отслеживает, как вы используете динамический массив, и освобождает связанную память, как только динамический массив больше не нужен. Что касается компилятора, связанная память больше не требуется, когда ваша функция существует.
Когда вы делаете:
Result := @ByteArr[0];
По сути, вы берете адрес для выделенного компилятором блока памяти. Поскольку для этого используется структура очень низкого уровня (Pointer
), компилятор не может отслеживать использование вами памяти, поэтому он освободит память, когда функция будет существовать. Это оставляет вас с указателем на нераспределенную память.
Как правильно вернуть Pointer
из функции
Прежде всего вы должны избегать указателей, если это возможно: они низкоуровневые, компилятор не может помочь с безопасностью типов или освобождением, их просто слишком легко ошибиться. А если вы неправильно указали указатели, то ошибки обычно являются нарушениями прав доступа, и их трудно отследить.
Тем не менее, если вы действительно хотите вернуть указатель, вы должны вернуть указатель на явно выделенную память, чтобы вы знали, что компилятор не освобождает его для вас. Когда вы сделаете это, убедитесь, что принимающий код знает, что он отвечает за память (следует освободить память, когда она больше не нужна). Например, ваша функция может быть переписана так:
function StreamToByteArray(Stream: TStream): Pointer;
begin
if Assigned(Stream) then
begin
Result := AllocMem(Stream.Size);
Stream.Position := 0;
Stream.Read(Result^, Stream.Size);
end
else
Result := nil;
end;
Как изменить указатель обратно на array of byte
или TStream
Ответ - нет способа вернуть его обратно. Указатель - это просто указатель на случайные данные. Массив байтов больше, чем данные, которые он содержит. TStream
является еще более абстрактным: это интерфейс, который говорит вам, как получить данные, он не обязательно содержит какие-либо данные. Например, TFileStream
(а равен a TStream
) не содержит любых байтов данных: все данные находятся в файле на диске.