[Решено] Две ошибки FreePascal

Вопросы программирования на Free Pascal, использования компилятора и утилит.

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

[Решено] Две ошибки FreePascal

Сообщение SilverShadow » 26.01.2016 07:50:56

В рамках тестирования считывателей флеш-карточек мною была сделана процедура чтения, поскольку выяснилось что ни copy ни xcopy не принимают в качестве параметра файла-приёмника NUL, как я делал обычно в FAR'е. Процедура тривиальнейшая, ничего особенного:
Код: Выделить всё
procedure DoReadFile(FileName:string);
var f:file of Byte;
    Buf: Array [1..32767] Of Byte;
    NumRead: Word;
begin
  assign(f,FileName);
  reset(f);
  repeat
    BlockRead(F, Buf, SizeOf(Buf), NumRead);
  until NumRead = 0;
  close(f);
end;

Каково же было моё удивление, когда я при выполнении этого кода словил эксцепшн Access denied. Какого-то лешего при считывании файла FreePascal открывает его на запись. WTF!?! - подумал я.

Ну как известно, у проблемы есть два решения - быстрое и неправильное, и медленное, трудоёмкое и правильное. Быстро - я снял со всех файлов атрибуты ReadOnly, и проблема исчезла. Медленно - потом переписал функцию с использованием CopyFileEx. Проблема исчезла совсем.

Всё же вопрос со скоростью операции BlockRead файла в большой буфер меня занимал, и я снова вернулся к этой функции. И уже на Array [1..131071] паскаль скрутил мне фигу, выдав предупреждение. Но мы тоже не лыком шиты, у нас же есть динамические массивы! И точно, с ними можно устанавливать размер хоть в 1048575. А вот тут меня и подстерегал очередной облом. Как честный ёжик, я считаю правильным освободить память после её использования. Но присвоение массиву значения NIL или же setlength его в 0(все операции взяты со странички FreePascal вики) приводило(и приводит) меня к SigSegv. Можно подумать, во всём виноваты слишком большие значения границ массива, но я наступил на грабли SigSegv даже с абсолютно рабочим значением 32767, которое без проблем проходит в статике. WTF!?!#2 - подумал я.

И вот второй WTF мне победить не удалось. Кто что посоветует?
Последний раз редактировалось SilverShadow 26.01.2016 14:51:28, всего редактировалось 1 раз.
SilverShadow
новенький
 
Сообщения: 22
Зарегистрирован: 10.03.2010 17:48:59

Re: Две ошибки FreePascal

Сообщение Снег Север » 26.01.2016 08:57:59

Для начала хорошо бы указать в какой версии фрипаскаля это получалось.
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2995
Зарегистрирован: 27.11.2007 16:14:47

Re: Две ошибки FreePascal

Сообщение iN0k » 26.01.2016 09:04:13

SilverShadow писал(а):Как честный ёжик, я считаю правильным освободить память после её использования.

правильный подход, однако в случаях с дин.Массивами необходимо быть аккуратным. У них присутствует хитрая система подсчета ссылок и несколько запутанные правила передачи в параметрах (то есть в некоторых случаях вы можете работать с КОПИЕЙ массива).

для буферов я предложил бы использовать один из следующих подходов
1. `Getmem` и `Freemem` (работа с ними более "низкоуровневая", зато и более очевидна).
2. одного из наследников TStream.
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Две ошибки FreePascal

Сообщение SilverShadow » 26.01.2016 11:58:19

Снег Север писал(а):Для начала хорошо бы указать в какой версии фрипаскаля это получалось.


Lazarus 1.4.2, FreePascal 2.6.4

Добавлено спустя 16 минут 44 секунды:
iN0k писал(а):с дин.Массивами необходимо быть аккуратным. У них присутствует хитрая система подсчета ссылок и несколько запутанные правила передачи в параметрах (то есть в некоторых случаях вы можете работать с КОПИЕЙ массива).

Если бы я куда-то передавал параметры, то да. Но ведь всё происходит в рамках одной процедуры!!!
Код: Выделить всё
procedure DoReadFile(FileName:string);
var f:file of Byte;
    Buf: Array Of Byte;
    NumRead: Word;
begin
  assign(f,FileName);
  reset(f);
  setlength(Buf,32767);
  repeat
    BlockRead(F, Buf, SizeOf(Buf), NumRead);
  until NumRead = 0;
  close(f);
  // ...что совой об пень
  //Buf:=NIL;
  setlength(Buf,0);
  // что пнём об сову
end;
SilverShadow
новенький
 
Сообщения: 22
Зарегистрирован: 10.03.2010 17:48:59

Re: Две ошибки FreePascal

Сообщение SSerge » 26.01.2016 12:27:16

SilverShadow писал(а): Какого-то лешего при считывании файла FreePascal открывает его на запись. WTF!?! - подумал я.


гм... Если подумать, а каким образом открыть типизированный файл на чтение-запись, если единственный метод, предусмотренный для этого - reset. :D

Description

Reset opens a file F for reading. F can be any file type. If F is a text file, or refers to standard I/O (e.g : '') then it is opened read-only, otherwise it is opened using the mode specified in filemode. If F is an untyped file, the record size can be specified in the optional parameter L. A default value of 128 is used. File sharing is not taken into account when calling Reset.


при этом "filemode" в соответствующей мурзилке - есть ни что иное как глобальная переменная с таким именем, а не оборот речи.
ЗЫ: поведение, известное с х.з. каких времен и турбы - тот тоже валится на попытках чтения read-only типизированных файлов, если не.
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Две ошибки FreePascal

Сообщение SilverShadow » 26.01.2016 12:37:04

SSerge писал(а): If F is a text file, or refers to standard I/O (e.g : '') then it is opened read-only, otherwise it is opened using the mode specified in filemode.

Спасибо, буду знать. Я как-то раньше с текстовыми файлами работал, и с таким поведением функции reset не сталкивался.

Добавлено спустя 2 часа 13 минут 5 секунд:
Ну что, решил я проблему. Дурил оператор SizeOf. Вот кусочек тестовой программы:
Код: Выделить всё
var pBuf: Pointer;
    aArrStatic: Array [1..32767] of Word;
    aArrDynamic: Array of Word;

begin
  GetMem(pBuf, 32767);
  writeln('SizeOf(pBuf) ', SizeOf(pBuf));
  writeln('SizeOf(pBuf^) ', SizeOf(pBuf^));
  writeln('SizeOf(aArrStatic) ', SizeOf(aArrStatic));
  writeln('SizeOf(aArrDynamic(only created)) ', SizeOf(aArrDynamic));
  SetLength(aArrDynamic,32767);
  writeln('SizeOf(aArrDynamic(after SetLength(aArrDynamic,32767))) ', SizeOf(aArrDynamic));

И его результаты:
Код: Выделить всё
SizeOf(pBuf) 4
SizeOf(pBuf^) 0
SizeOf(aArrStatic) 65534
SizeOf(aArrDynamic(only created)) 4
SizeOf(aArrDynamic(after SetLength(aArrDynamic,32767))) 4


Не кажется ли вам, что такое поведение SizeOf достойно упоминания на багтрекере?
SilverShadow
новенький
 
Сообщения: 22
Зарегистрирован: 10.03.2010 17:48:59

Re: [Решено] Две ошибки FreePascal

Сообщение Mirage » 26.01.2016 15:40:27

SizeOf() возвращает размер переменной. Для указателей (а дин. массив это тоже указатель) на 32-бит системах это 4. Т.е. все верно.
BlockRead(F, Buf, SizeOf(Buf), NumRead);

SigSegv потому, что этот код запишет 4 байта поверх указателя, а не там, куда он указывает.
Надо так:
Код: Выделить всё
BlockRead(F, Buf[0], 32767, NumRead);
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: [Решено] Две ошибки FreePascal

Сообщение SAK » 28.01.2016 14:39:11

И ещё почему 32767, а не 32768? 32 кБ это 32768 байт. объявление будет: array[0..32767] of byte или array[1..32768] of byte.
SAK
постоялец
 
Сообщения: 158
Зарегистрирован: 18.02.2006 00:45:14
Откуда: Тим

Re: [Решено] Две ошибки FreePascal

Сообщение Лекс Айрин » 28.01.2016 17:54:00

SAK, потому что отсчет идет с нуля... это общеизвестный факт.
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград

Re: [Решено] Две ошибки FreePascal

Сообщение mig-31 » 28.01.2016 17:54:45

Оба правильно.
mig-31
постоялец
 
Сообщения: 224
Зарегистрирован: 14.07.2011 13:46:48

Re: [Решено] Две ошибки FreePascal

Сообщение Дож » 28.01.2016 18:30:45

Лекс Айрин писал(а):SAK, потому что отсчет идет с нуля... это общеизвестный факт.

Почитайте код, там отсчёт с 1.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: [Решено] Две ошибки FreePascal

Сообщение Лекс Айрин » 28.01.2016 19:51:42

Дож, а компилятор об этом в курсе?
Тут еще надо знать счетчик массива знаковый или беззнаковый... есть подозрение, что первое -- тогда больше 32767 не адресуешь. (у меня подозрение, что счетчик это переменная типа integer, причем, не перекрытая до 4х байт)
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград

Re: [Решено] Две ошибки FreePascal

Сообщение SAK » 29.01.2016 10:23:48

Лекс Айрин писал(а):SAK, потому что отсчет идет с нуля... это общеизвестный факт.

А если глаза протереть?
SilverShadow писал(а): Buf: Array [1..32767] Of Byte;

SilverShadow писал(а):setlength(Buf,32767);

SilverShadow писал(а):aArrStatic: Array [1..32767] of Word;

SilverShadow писал(а): GetMem(pBuf, 32767);

И ни одного объявления с длиной буфера 32768.

Лекс Айрин писал(а):Тут еще надо знать счетчик массива знаковый или беззнаковый... есть подозрение, что первое -- тогда больше 32767 не адресуешь.

Упаси Бог пользоваться программами написанными программистами-теоретиками.
SAK
постоялец
 
Сообщения: 158
Зарегистрирован: 18.02.2006 00:45:14
Откуда: Тим

Re: [Решено] Две ошибки FreePascal

Сообщение Лекс Айрин » 29.01.2016 12:25:34

SAK писал(а):А если глаза протереть?


А если подумать? Счетчику без разницы откуда начинается отсчет. У него своя нумерация.

SAK писал(а):Упаси Бог пользоваться программами написанными программистами-теоретиками.



Пользуетесь же))) У меня просто нет возможности и желания проверять сей факт. И, кстати, зря вы так... большая часть проблем возникает как раз из-за того что перед тем как воплощать программу не задумываются о том как это лучше сделать. А ведь есть определенный цикл создания программного продукта. постановка ТЗ -- написание (создание) тестов -- написание программы -- отладка -- сдача в эксплуатацию. Без теоретиков на начальном этапе не обойтись.
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград

Re: [Решено] Две ошибки FreePascal

Сообщение Vadim » 30.01.2016 16:33:33

Лекс Айрин писал(а): А ведь есть определенный цикл создания программного продукта.

Оффтоп.
По-моему это давно уже кануло в Лету. У кого ни спрашиваю - все при словах "создание программы" подразумевают, что надо немедленно садиться за комп и с бешеной скоростью стучать по клавишам (или возюкать мышкой), как слетевший с катушек кодогенератор. А потом, когда пытаешься исправить ошибки такой "кодогенерации" понимаешь, что проще самому с нуля написать.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

След.

Вернуться в Free Pascal Compiler

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

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

Рейтинг@Mail.ru