Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
RAPTORGrrr
Зарегистрирован: 11.09.2007 Сообщения: 2
|
Добавлено: Вт Сен 11 2007 12:29 Заголовок сообщения: Проблемы с указателями в Delphi |
|
|
Здравствуйте!
Есть такая проблема:
По com-порту получаю пакет данных, и определяю его тип по заголовку.
Всего есть пять различных пакетов, и все они могут содержатся в одной посылке данных.
Подумал что быстрее будет, если использую указатели, т.е. найду в памяти нужный кусок данных
и типизую его к своему типу TTargetPackage.
Сделал так:
var PtrData : pointer;
GetMem(PtrData, 75 );
PtrData := @Answer[0]; // здесь Answer динамический массив, который содержит нужный кусок данных
Target := TTargetPackage(PtrData^); // это вроде как типизация
FreeMem(PtrData); // ВОТ ТУТ ругается Invalid pointer operatoin
PtrData:=nil;
// TTargetPackage - это запись;
// Target - принадлежит типу TTargetPackage
Почему?
Где что не так? |
|
Вернуться к началу |
|
|
igor406
Зарегистрирован: 27.06.2007 Сообщения: 11
|
Добавлено: Ср Сен 12 2007 12:34 Заголовок сообщения: |
|
|
to RAPTORGrrr
Цитата: |
PtrData := @Answer[0]; // здесь Answer динамический массив, который содержит нужный кусок данных
Target := TTargetPackage(PtrData^); // это вроде как типизация |
проблема в том, что не кусок данных, а весь массив ты пытаешся привести к типу TTargetPackage, а во вторых, TTargetPackage - это class или record? |
|
Вернуться к началу |
|
|
RAPTORGrrr
Зарегистрирован: 11.09.2007 Сообщения: 2
|
Добавлено: Ср Сен 12 2007 14:46 Заголовок сообщения: |
|
|
Спасибо за внимание, igor406!
По поводу типов
Answer определен как TPack = packed array of byte.
TTargetData - это запись.
TTargetData = packed record
Breadth : single;
Longitude : single;
RAW : array [1..8] of single;
end;
Не хотел нагружать подробностями кода, но если без этого никак...
Вот что в исходнике.
function TfrmMain.AnalysePackage ( Answer : TPack): word;
var
PtrData : Pointer;
Position : word;
EndPackage : word;
begin
Position:=0; // текущая позиция в пакете
while Position <= High(Answer) do // проходим по всему пакету
begin
case Answer[Position] of // Проверяем заголови
TargetID : // пакет целевых данных
begin
EndPackage:= Position+SizeTarget-1; // Определяем конечную позицию
GetMem(PtrData,SizeTarget); // SizeTarget - 75 байт
PtrData := @Answer[Position];
Target:= TTargetPackage(PtrData^);
Freemem(PtrData);
PtrData:=nil;
Position:=Position+SizeTarget+1; // Установка позиции на след. байт
TargetUpdate:= true; // признак обновления данных
Result:=1;
end;
AirborneSystemStatusID: // пакет статуса системы
begin
// Аналогично
end;
MatterID : // пакет веществ
begin
// Аналогично
end;
TimeDateID : // пакет времени
begin
// Аналогично
end;
EvolutionID : // пакет крен/тангаж
begin
// Аналогично
end;
MotionID : // пакет курс/высота
begin
// Аналогично
end;
else exit;
end; // case
end; // while новый проход по пакету
end;
Пример:
Answer содержит 55 5D 42 25 06 2D 41 D5 78 C9 ... BF CF
Начиная с адреса @Answer[0] (значение 55) память содержит нужные мне 75 байт.
Я в PtrData копирую этот адресс, и выделяю память Getmem'ом под них. Затем типизую.
До этого все идет хорошо и в Target содержатся верные данные. Но когда освобождаю память, то вылетает ошибка - Invalid pointer operation.
Вроде бы простая операция: занял память Getmem, освободил FreeMem, но вот затыка - именно на FreeMem прога валится с ошибкой Invalid pointer operation.
А если сделать так:
PtrData:=nil;
Freemem(PtrData);
то все нормально, кроме того что потерял кусок памяти.
Что делать? |
|
Вернуться к началу |
|
|
igor406
Зарегистрирован: 27.06.2007 Сообщения: 11
|
Добавлено: Ср Сен 12 2007 15:35 Заголовок сообщения: |
|
|
to RAPTORGrrr
GetMem и FreeMem работают с линейными непрерывными кусками памяти.
Потому использовать их надо при работе с простыми типами.
И зачем так заморачиваться, если при выходе из процедуры память автоматически отводимая под простые локальные переменные при их объявлении возвращается в кучу (освобождается).
Единственно, когда её нужно освобождать самостоятельно - это при работе с объектами.
Ну, а если всёже необходимо выделять и освобождать память под объекты типа рекордов (но отнюдь не для классов), то необходимо использовать процедуры New и Dispose, хотя на самом деле всё это пережитки прошлого, и при нормальной организации структуры программы на современном этапе необходимости в этом практически нет. |
|
Вернуться к началу |
|
|
критикан
Зарегистрирован: 18.02.2005 Сообщения: 247
|
Добавлено: Чт Сен 13 2007 08:29 Заголовок сообщения: Кто дурнее: Паскаль: нельзя 'A'=65 или Си: нужно 'A'==65? |
|
|
RAPTORGrrr писал(а): | Здравствуйте!
Сделал так:
var PtrData : pointer;
GetMem(PtrData, 75 );
PtrData := @Answer[0]; // здесь Answer динамический массив, который содержит нужный кусок данных
Target := TTargetPackage(PtrData^); // это вроде как типизация
FreeMem(PtrData); // ВОТ ТУТ ругается Invalid pointer operatoin
PtrData:=nil;
// TTargetPackage - это запись;
// Target - принадлежит типу TTargetPackage
Почему?
Где что не так? |
Причина очень простая: после выполнения
GetMem(PtrData, 75)
указатель PtrData содержит адрес выделенной области памяти (это называется "указывает на неё"), а после выполнения (ВНИМАНИЕ!)
PtrData := @Answer[0]
указатель PtrData содержит ДРУГОЙ адрес -- он указывает не на память, выделенную оператором GetMem(PtrData, 75), а на память для переменной Answer. Соответственно, команда
FreeMem(PtrData)
пытается освободить совершенно другую память.
Чтобы проверить эти соображения, нужно посмотреть, что содержится в переменной PtrData сразу после GetMem(PtrData, 75) и после PtrData := @Answer[0]. Это будут разные числа -- адреса РАЗНЫХ участков памяти. Освобождать можно область по адресу, полученному после GetMem(PtrData, 75), а не после PtrData := @Answer[0]
Это диагноз.
Теперь лечение.
Никаких GetMem/FreeMem не нужно, так как указатель PtrData используется для адресации уже выделенных участков памяти: память была выделена при определении переменных Answer и Target. и происходит просто копирование информации из одного (уже имеющегося) участка в (уже имеющийся) другой. Паскалисты, умрите от зависти: в Си это делается просто копированием одного участка памяти в другой, так как типизация выполняется УКАЗАНИЕМ типа, а не ВВОДОМ специальной переменной
-------------------------------------------------------------------
Ну и кто дурнее: Паскаль с недопустимым 'A'=65 или Си с обычным 'A'==65? |
|
Вернуться к началу |
|
|
|