Добрый день!
Помогите разобраться с причиной возникновения ошибки 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 происходит та же история.
Помощь с Exception SIGSEGV
Модератор: Модераторы
Пошаговая отладка не помогает. Ошибка вылезает в бессмысленном месте 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].
Дож, спасибо за ответ!
Вообще я несилен в указателях, идею понимаю, но использовать не получается. Эти строки кода у меня тоже вызывали сомнение, потому что в таком объявлении происходит копирование адреса 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-му элементу массива:
Если так, то получается поэлементное считывание из ArByte в ArSmallInt? Нельзя ли считать весь массив, как я писал выше? И вообще, можно было бы обойтись без указателей?
Вообще я несилен в указателях, идею понимаю, но использовать не получается. Эти строки кода у меня тоже вызывали сомнение, потому что в таком объявлении происходит копирование адреса 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? Нельзя ли считать весь массив, как я писал выше? И вообще, можно было бы обойтись без указателей?
Всё верно до этого момента:
Я предлагаю вообще отказаться от ArSmallInt и всех схожих промежуточных массивов и написать так:
Можно безопасно сэмулировать то, что вы делаете в своём коде, как-то так:
Ну так уж, чтоб прям совсем, навряд ли — или придётся для каждого DataType'а отдельно алгоритм продублировать.
Добавлено спустя 21 минуту 17 секунд:
Можно воспользоваться классом TMemoryStream. Сперва записать в него данные при помощи Write, потом выставить Position в 0 и дальше читать как из обычного стрима:
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.
Дож, Спасибо большое!
Теперь все понятно. Попробую все 3 варианта в познавательных целях.
Добавлено:
Реализовал 1-ый и 3-ий вариант. Код 1-го варианта значительно компактнее и интереснее. 3-ий вариант близок к моему замыслу.
Вопрос решен.
Теперь все понятно. Попробую все 3 варианта в познавательных целях.
Добавлено:
Реализовал 1-ый и 3-ий вариант. Код 1-го варианта значительно компактнее и интереснее. 3-ий вариант близок к моему замыслу.
Вопрос решен.
