Ошибка при изменении длины динамического массива

Вопросы программирования и использования среды Lazarus.

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

Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Ошибка при изменении длины динамического массива

Сообщение Alex. S »

Отлаживаю dll, в которой есть процедуру, в которую я в качестве переменной передаю массив из программы. Проблема в том, что когда длина массива устанавливается в функции dll, программа завершается с ошибкой External "SIGSEGV". Если установить длину массива до вызова функции, в самой программе, всё нормально...

Использую такую структуру:
Часть кода из DLL:

Код: Выделить всё

...
TDWordArray = array of Cardinal;
...
procedure GetData(var Data: TDWordArray);
begin
//
SetLength(Data, 1);
//
Data[0]:=5;
end;


Частичный код программы:

Код: Выделить всё

...
type
//
  TDWordArray = array of Cardinal;

...
procedure GetData(Data: TDWordArray); external 'MyDLL.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
//
i, k: Cardinal;
//
SomeData: TDWordArray;
begin
//
GetData(SomeData);

//
k:=Length(SomeData);
//
if k > 0 then
  begin
  //
  Dec(k);

  //
  for i:=0 to k do
  //
  ShowMessage(IntToStr(SomeData[i]));
  end;
end;
Аватара пользователя
Sharfik
энтузиаст
Сообщения: 836
Зарегистрирован: 20.07.2013 01:04:30

Сообщение Sharfik »

Не уверен, но может сработает так

Код: Выделить всё

procedure TForm1.Button1Click(Sender: TObject);
var
//
i, k: Cardinal;
//
SomeData: TDWordArray;
begin
//
SetLength(SomeData,0);// Добавить эту строку

GetData(SomeData);

//
k:=Length(SomeData);
//
if k > 0 then
  begin
  //
  Dec(k);
  //
  for i:=0 to k do
  //
  ShowMessage(IntToStr(SomeData[i]));
  end;
end;
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Sharfik, увы, всё равно ошибка :(
Аватара пользователя
Sharfik
энтузиаст
Сообщения: 836
Зарегистрирован: 20.07.2013 01:04:30

Сообщение Sharfik »

1) Передавать SomeData безтолку, если он не инициализирован ранее.
2) DLL это новый поток/класс/область Application. Данные созданные Application относящемуся к DLL, не видны в основном Application. Но из основного видны в DLL.
Я не делал интерфейсы, поленился. Я создал абстрактные классы, и ими подгружая их в библиотеки манипулирую как куклой само приложение.
Можно создать класс TCustomArrayController и от него TArrayController. исходники первого прицепить в библиотеку.

Добавлено спустя 13 минут 21 секунду:
туплю...
Data: TDWordArray это переменная хранящая данные типа TDWordArray, и делая
procedure GetData(Data: TDWordArray); external 'MyDLL.dll';
передается копия данных, а не указатель на область памяти. Даже если она была инициализирована. И то кажется массивы передавать нельзя так, только через Record(где то встречал).
Когда передаются классы, то переменная передаваемая хранит адрес памяти объекта, а не все данные класса.
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Sharfik, мысль про передачу SomeData вроде понял, а вот про TCustomArrayController не очень. Попробую завтра реализовать через классы, посмотрю, что получится :)
Аватара пользователя
Sharfik
энтузиаст
Сообщения: 836
Зарегистрирован: 20.07.2013 01:04:30

Сообщение Sharfik »

TCustomArrayController другие меня помидорами закидают, но если нужен именно массив, то можно сделать класс который будет создаваться в нужный момент и управлять данными в массиве. Правильнее вроде с интерфейсами, но я в них не понимаю ничего.
Вот этот класс надо как модуль загрузить в проект и программы и библиотеки. Они должны быть идентичными. Класс описывается абстрактно в одном модуле, а сами процедуры реализуются во втором модуле, который не обязательно передавать в библиотеку.
Интерфейсы по сути делают тоже самое, что то типа абстрактного описания классов программы.
resident
энтузиаст
Сообщения: 605
Зарегистрирован: 13.03.2013 16:58:51

Сообщение resident »

А если в программу возвращать длину?

Код: Выделить всё

procedure GetData(var Data: TDWordArray; var DataLength: integer);
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Sharfik, это не Вы тупите, а я. Упустил слово var перед параметром процедуры.

Про вариант, который Вы предложили, я такого ещё не встречал. Мне уже действительно легче как resident предложил, вначале получать длину массива, устанавливать её, и потом заполнять его данными :lol: Обидно, что придётся тогда две процедуры прогонять, вместо одной. Снижается удобность :P

Ещё вопрос, пусть даже сделаю на интерфейсах, как потом со совместимостью с другими языками? Или получится, Object Pascal only? ;) Я и так думаю, сейчас уже думаю, получится ли использовать dll библиотеку, при разработке на Си и других языках...

P.S. Хотя я ж мучился, когда подключал чужие библиотеки, которые на Си писали, или ещё на чём-то, пусть теперь они мучаются, пытаясь мою подключить :mrgreen: В знак солидарности им...

P.S.S. Вообще, пришёл к выводу, что чем дольше я программирую, тем большее понимаю, что ничего не знаю толком ещё :)
Аватара пользователя
Sharfik
энтузиаст
Сообщения: 836
Зарегистрирован: 20.07.2013 01:04:30

Сообщение Sharfik »

Классы или массивы - Object Pascal only
Если интерфейсы - тогда есть шанс, но надо использовать типы данных которые существуют в других языках.

Тебе стоит день потратить на блог GunSmoker-а, он много про плагины на Delphi писал.
http://www.gunsmoker.ru/2011/12/delphi.html
Правило номер шесть - вы не должны использовать типы данных Delphi, потому что они не имеют аналога в других языках. Например, string, array of, TObject, TForm (и вообще любые объекты и уж тем более компоненты) и т.п. Что можно использовать - целочисленные типы (Integer, Cardinal, Int64, UInt64, NativeInt, NativeUInt, Byte, Word и т.п.; я бы не рекомендовал использовать Currency, если только он вам действительно нужен), вещественные (Single и Double; я бы рекомендовал избегать типов Extended и Comp, если только они действительно вам нужны и иначе никак), перечислимые и subrange-типы (с некоторыми оговорками), символьные типы (AnsiChar и WideChar, но не Char), строки (только в виде WideString), логический тип (BOOL, но не Boolean), интерфейсы (interface), в методах которых используются допустимые типы, записи (record) из вышеуказанных типов, а также указатели на них (в том числе указатели на массивы из вышеуказанных типов, но не динамические массивы).
Mirage
энтузиаст
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia
Контактная информация:

Сообщение Mirage »

Alex. S: Как работают дин. массивы знаете? Про подсчет ссылок и т.п.
Надо, чтобы память, выделяемая в dll там же и освобождалась. А память, выделяемая в основном приложении освобождалась в нем же, т.к. менеджеры памяти по умолчанию разные. Если передавать дин. массив как var параметр вообще непонятно что получится. Основное приложение рано или поздно попытается освободить память в таком массиве и скорее всего упадет, если эту память не оно же выделяло.
Решение - хорошо подумать, нужна ли тут dll. Скорее всего нет.
Если таки нужна, то не передавать между приложением и .dll сущности с автоматически управляемым временем жизни. Т.е. строки, дин. массивы, интерфейсы (впрочем, они все равно специфичны для Паскаля). А передавать буферы, выделенные в куче и зорко следить за их правильным освобождением.
Насколько проблему решает общий менеджер памяти не проверял. И как он сказывается на возможности использовать эту dll из других языков тоже надо бы проверить.
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Sharfik, Mirage, спасибо Вам за разъяснения...

В таком случае, буду делать процедуру получения количества элементов, далее, выделять память для указателя на массив, и передавать этот указатель, а не изменять длину массива, и передавать его. Если я равильно понял, так гораздо лучше...

Ещё пара вопросов:
1) В описании параметров нескольких процедур, я использую const, я так понимаю, его тоже лучше убрать... То есть, оставить передачу либо с var, либо без ничего...

2) Как правильно передать имя файла в функцию dll? Раньше использовал String, а как сейчас, даже не знаю.

P.S. Если просто поменять тип переменной результата с Boolean на Bool, но в программе присваивать этот результат переменной типа Boolean, это же нормально? Я знаю, что у этих типов различается значение для true, но, насколько понимаю, это не будет влиять на возможность получения ошибки...
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

Alex. S писал(а):В описании параметров нескольких процедур, я использую const, я так понимаю, его тоже лучше убрать...


А это зависит от ситуации. Иногда лучше оставить.
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Лекс Айрин, а на других языках программирования потом проблемы не будет, из-за этого?

Прочитал, статью GunSmoker'а. Нашёл, что вместо String нужно использовать WideString, хотя ниже, в его коде используется String, а про WideString и упоминания нет :( Так как же правильно?
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

Alex. S, не должна бы быть. Var и Const в описании функций/процедур это скорее напоминание изменяется параметр или нет внутри подпрограммы. Для функций, кстати, хорошим тоном считается отсутствие побочных эффектов... т.е. параметры не должны изменяться, только результат. Для процедур, это не так, но все же желательно уменьшить побочные эффекты.
Последний раз редактировалось Лекс Айрин 22.02.2016 11:17:46, всего редактировалось 1 раз.
Alex. S
новенький
Сообщения: 39
Зарегистрирован: 22.08.2015 10:37:00

Сообщение Alex. S »

Лекс Айрин, ясно, спасибо :)

Кстати, как правильно определить Bool, а не Boolean переменную? Это будет WordBool или LongBool?
Ответить