Помощь с Exception SIGSEGV

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

Помощь с Exception SIGSEGV

Сообщение serge#LVL » 19.08.2016 14:09:09

Добрый день!
Помогите разобраться с причиной возникновения ошибки Exception SIGSEGV при исполнении программы.

Задача простая: считать из файлов таблицу х, y, z для дальнейших действий. х, y, z хранятся в двух файлах каждый: в бинарном *.img и метафайле *.hdr. Метафайл хранит размерность массива, тип данных и др.

Написаны 2 функции:
1) function ReadHDR(AName: string; var Ahdr: THDR): boolean; - считывает hdr-файл AName и записывает его данные в структуру Ahdr.
2) function ReadIMG(AName: string; Ahdr: THDR; var AValue: TTwoDimArofReal): boolean; - считывает двумерный массив AValue из img-файла AName по структуре Ahdr.

Эта пара функций последовательно вызывается для считывания z. Пользователь указывает в диалоге имя файла, где хранится z.
Для теста даны 6 файлов (longitude.hdr, longitude.img), (latitude.hdr, latitude.img), (Rrs_443.hdr, Rrs_443.img).

Вводится любой из img-файлов, функции и программа работают нормально, данные считываются правильно.

Теперь ВНИМАНИЕ! Чтобы сформировать таблицу х, y, z, в программа 3 раза обращается к описанным функциям. Вначале для считывания z (в тесте Rrs_443.img), который указывает пользователь, а потом - для считывания x и y (уже без участия пользователя). И здесь происходит ошибка. Т.е. получается, что по отдельности х, y, z считываются, а вместе уже нет.

Пошаговая отладка не помогает. Ошибка вылезает в бессмысленном месте str1:=StringReplace(AStr,#32,'',[rfReplaceAll]); - на строке, в которой происходит удаление из переменной типа string пробелов. Вообще, пробуя разные варианты ввода обнаружил, что ошибка вылезает в разных местах кода. Такое у меня раньше случалось при ошибке в использовании динамических массивов в Delphi. Выходило, что компилятор выполнял ошибочное действие, а ошибку выдавал в совершенно другом месте.

В Lazarus всплывает окно Ассемблера с указанием адреса памяти, по которому произошла ошибка. Но Ассемблером пользоваться я не умею. Уже крыша едет, нужна помощь в отыскании ошибки. Код программы прилагаю - https://yadi.sk/d/U1HJzhzfuL9Ek
Lazarus 1.6/ Linux Mint Rosa. При компиляции в Windows7 происходит та же история.
serge#LVL
новенький
 
Сообщения: 13
Зарегистрирован: 19.08.2016 13:17:36

Re: Помощь с Exception SIGSEGV

Сообщение Дож » 19.08.2016 21:01:02

Пошаговая отладка не помогает. Ошибка вылезает в бессмысленном месте str1:=StringReplace(AStr,#32,'',[rfReplaceAll]); - на строке, в которой происходит удаление из переменной типа string пробелов. Вообще, пробуя разные варианты ввода обнаружил, что ошибка вылезает в разных местах кода.

Это стандартная ситуация для SIGSEGV. В одном месте программы у вас портится память, в другом месте программа падает из-за того, что указатели смотрят куда-то не туда.

Поиск таких ошибок — творческий процесс, нужно «руками и глазами» отследить в какой момент совершается операция, которая приводит к непредвиденной порче памяти.

Код: Выделить всё
Pointer(ArSmallInt):=@ArByte[0];
<...>
Pointer(ArLongInt):=@ArByte[0];
<...>
Pointer(ArSingle):=@ArByte[0];
<...>
Pointer(ArDouble):=@ArByte[0];
<...>
Pointer(ArWord):=@ArByte[0];
<...>
Pointer(ArLongWord):=@ArByte[0];
<...>
Pointer(ArInt64):=@ArByte[0];
<...>
Pointer(ArQWord):=@ArByte[0];


Так писать нельзя — FPC хранит число ссылок на динамический массив, т.к. вы скопировали указатели по-тихому, счётчик не проинкрементируется, участок памяти с данными массива ArByte будет освобождён дважды.

Я бы порекомендовал писать smallint(Pointer(@ArByte[i * NumBytes])^) вместо приведения дин. массива и ArSmallInt[i].
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Помощь с Exception SIGSEGV

Сообщение serge#LVL » 19.08.2016 21:54:01

Дож, спасибо за ответ!

Вообще я несилен в указателях, идею понимаю, но использовать не получается. Эти строки кода у меня тоже вызывали сомнение, потому что в таком объявлении происходит копирование адреса ArByte, а не его значений, а нужно было именно скопировать значения.

По задумке хотелось сделать так, как при считывании массива из потока данных: FS.Read( Pointer(ArSmallInt[0])^, size1), т.е. считать size1-байт из ArByte,
но как правильно (и можно ли вообще составить) такую конструкцию не знаю. Я пробовал что -то такое: Pointer(ArSmallInt[0])^ := Pointer(ArByte[0])^ .
Не подскажите, можно такое сделать?

Поясните мне еще, как правильно трактовать ваш пример: smallint(Pointer(@ArByte[i * NumBytes])^)
@ArByte[i * NumBytes] - это адрес первого байта, с которого считывается smallint;
Pointer() - приведение к нетипизированному указателю;
Pointer() ^ - разыменывание этого указателя (или в обратном порядке?);
smallint() - приведение к типу smallint, т.е. от первого байта будет считано 2 байта.

Тогда это выражение должно быть присвоено i-му элементу массива:
Код: Выделить всё
ArSmallInt[i]:=smallint(Pointer(@ArByte[i * NumBytes])^);

Если так, то получается поэлементное считывание из ArByte в ArSmallInt? Нельзя ли считать весь массив, как я писал выше? И вообще, можно было бы обойтись без указателей?
serge#LVL
новенький
 
Сообщения: 13
Зарегистрирован: 19.08.2016 13:17:36

Re: Помощь с Exception SIGSEGV

Сообщение Дож » 19.08.2016 22:05:59

Всё верно до этого момента:
serge#LVL писал(а):Тогда это выражение должно быть присвоено i-му элементу массива:
Код: Выделить всё
ArSmallInt[i]:=smallint(Pointer(@ArByte[i * NumBytes])^);

Если так, то получается поэлементное считывание из ArByte в ArSmallInt?

Я предлагаю вообще отказаться от ArSmallInt и всех схожих промежуточных массивов и написать так:
Код: Выделить всё
        2: begin
              for i:=0 to Ahdr.ncols-1 do
              AValue[j,i]:=smallint(Pointer(@ArByte[i * NumBytes])^)*Ahdr.gain+Ahdr.offset;
           end;


Нельзя ли считать весь массив, как я писал выше?

Можно безопасно сэмулировать то, что вы делаете в своём коде, как-то так:
Код: Выделить всё
  // другое объявление типа
  POneDimArOfSmallInt = ^TOneDimArOfSmallInt;
  TOneDimArOfSmallInt = array[0..1] of smallint;
  ...
  // другое объявление переменной
  ArSmallInt: POneDimArOfSmallInt;
  ...
  // теперь можно присвоить указатель
  ArSmallInt := POneDimArOfSmallInt(@ArByte[0]);
  // и обращаться к ArSmallInt^[I]


И вообще, можно было бы обойтись без указателей?

Ну так уж, чтоб прям совсем, навряд ли — или придётся для каждого DataType'а отдельно алгоритм продублировать.

Добавлено спустя 21 минуту 17 секунд:
По задумке хотелось сделать так, как при считывании массива из потока данных: FS.Read( Pointer(ArSmallInt[0])^, size1), т.е. считать size1-байт из ArByte,
но как правильно (и можно ли вообще составить) такую конструкцию не знаю. Я пробовал что -то такое: Pointer(ArSmallInt[0])^ := Pointer(ArByte[0])^ .
Не подскажите, можно такое сделать?

Можно воспользоваться классом TMemoryStream. Сперва записать в него данные при помощи Write, потом выставить Position в 0 и дальше читать как из обычного стрима:
Код: Выделить всё
uses
  SysUtils, Classes;
const
  B: array[0..9] of Byte = (2, 0, 4, 0, 6, 0, 8, 0, 10, 0);
var
  M: TMemoryStream;
  S: array of SmallInt;
  I: LongInt;
begin
  M := TMemoryStream.Create;
  M.Write(B[0], Length(B));
  M.Position := 0;

  SetLength(S, Length(B) div SizeOf(SmallInt));
  M.Read(S[0], SizeOf(SmallInt) * Length(S));
  for I := 0 to High(S) do
    Writeln(S[I]);
  FreeAndNil(M);
end.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Помощь с Exception SIGSEGV

Сообщение serge#LVL » 19.08.2016 22:34:17

Дож, Спасибо большое!

Теперь все понятно. Попробую все 3 варианта в познавательных целях.

Добавлено:

Реализовал 1-ый и 3-ий вариант. Код 1-го варианта значительно компактнее и интереснее. 3-ий вариант близок к моему замыслу.

Вопрос решен.
serge#LVL
новенький
 
Сообщения: 13
Зарегистрирован: 19.08.2016 13:17:36


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru