Страница 1 из 1

Длина массива, различие в Delphi и FPC[Решено]

СообщениеДобавлено: 15.03.2011 15:11:26
Maxizar
Динамический массив, область памяти, на которую указывает переменная данного типа. Но это так же чуть больше чем просто массив, у динамического есть еще длина и счетчик ссылок.
Так вот скажем процедура Length возвращает длину массива и в Delphi и в FPC одну и туже Ура.
Когда же мы пытаемся узнать ее средствами ASM, тобишь копаясь в памяти, я получил такую не приятную вещь, FPC хранит в памяти где лежит Length, число которое меньше чем вернет процедура Length на 1.

Предположим имеем такой тип данных:
Код: Выделить всё
TIntArray2 = array  of integer;

Напишем процедуру тестирования:
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
var D1:TIntArray2;
begin
    SetLength(D1,5);
   caption:=IntToStr(Len(D1))+' '+IntToStr(Length(D1));
end;

Где процедура Len написана нами на асме:
Для Delphi 2009:
Код: Выделить всё
function Len(var D:TIntArray2):Integer; register;assembler;
asm
  mov eax,[eax]
  mov eax,[eax-4]
end;


И для FPC:
Код: Выделить всё
function Len(var D:TIntArray2):Integer; register;assembler;
{$ASMMODE intel}
asm
  mov eax,[eax]
  mov eax,[eax-4]
end; 


Проводим тест, и получаем результат:
Delphi: Length(D1)=5, Len(D1)=5.
FPC: Length(D1)=5, Len(D1)=4.
Вопрос: Почему?

Lazarus 0.9.29 FPC 2.4.2.

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 15.03.2011 15:38:22
Sergei I. Gorelkin
В FPC это то значение, которое возвращает ф-ция high() - на единицу меньше чем length.

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 15.03.2011 15:57:34
Maxizar
Да получается, что так, зачем они так сделали не понятно.. Теперь получается чтобы вернуть, указатель на динамический массив, который скажем сгенерировала функция из DLL написанная на Delphi, просто так не получится, нужно будет подправить, число которое записано по смещение -4 от-но указателя на массив...
Мда, зачем разработчики, делают отличия, в тех местах, где вроде бы нужно стремится сделать тютелька в тютельку...

А заметил то, чисто случайно. Переписываю процедуру для БПФ, на асм.. и тут бабах... фигня такая, прочитал книги, которые есть по сабжу, полез в гугл, везде длина массива.. а тут High получается... Обидно :( :evil:
Смысл теперь юзать Delphi mod.... Вот такими мелочами, меня растраивают разработчики FPC :( :? :(

Добавлено спустя 17 минут 11 секунд:
Но {$MODE DELPHI} не помогает... Мдя... либо последний не корректно работает в этом случае ((

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 16.03.2011 00:11:23
devels
Чем вас не устраивает обычный Length ? Или он медленнее?

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 16.03.2011 01:01:00
Maxizar
devels писал(а):Чем вас не устраивает обычный Length ? Или он медленнее?

Тем, что я вообще не хотел вызывать процедуру Length в своем асм коде... она просто лишняя, и думал справится простым обращением через смещение -4. для динамического массива.
Но получается, что так нельзя делать, если данный код будет находится в ДЛЛ и работать с динамичискими массивами, которы будут создаваться в другом языке отличным от Free Pascal.
Пример:
DLL в которой одна функция суммирующая элементы массива:

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

{$mode objfpc}{$H+}

uses
  Classes
  { you can add units after this };

type
TIntArray2 = array  of integer;


{$R *.res}

function Sum1(var A:TIntArray2):Integer;Register;assembler;
{$ASMMODE intel}
asm
   mov EAX,[EAX]
   mov ECX,[EAX-4]
   //dec ECX
   mov EDX,[EAX]
@Summ:
   add EAX,4
   add EDX,[EAX]
   loop @Summ
   mov EAX,EDX
end;

Exports
Sum1 index 1;
begin
end.

Вызываем ее в программе написанной на Lazarus:
Код: Выделить всё
unit Unit1;

{$mode objfpc}
//{$MODE DELPHI}
{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button2Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

   TIntArray2 = array  of integer;
var
  Form1: TForm1;

implementation
function Sum1(var A:TIntArray2):Integer;Register;  External 'project2.dll';

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button2Click(Sender: TObject);
var D1:TIntArray2;
begin
    SetLength(D1,3);
    D1[0]:=1;
    D1[1]:=2;
    D1[2]:=3;
     caption:=IntToStr(Sum1(D1));
end;
end.

Тестируем, видим что отработали хорошо, в название формы вернули число 6;
Теперь хотим использовать данную Dll в программе написанной на Delphi:
Код: Выделить всё
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

    TIntArray2 = array  of integer;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function Sum1(var A:TIntArray2):Integer;Register;  External 'project2.dll';


procedure TForm1.Button1Click(Sender: TObject);
var D1:TIntArray2;
begin
    SetLength(D1,3);
    D1[0]:=1;
    D1[1]:=2;
    D1[2]:=3;

    caption:=IntToStr(Sum1(D1));

end;
end.

Видим вернули чушь. А все потому что FPC и Delphi генерируют в памяти динаммические массивы один в один, кроме поля Length. по смещение -4 относительно указателя (переменной) массива.
Тобишь чтобы библиотека работала вместе с Делфи, нужно ее переписать вот так:
Код: Выделить всё
function Sum1(var A:TIntArray2):Integer;Register;assembler;
{$ASMMODE intel}
asm
   mov EAX,[EAX]
   mov ECX,[EAX-4]
   dec ECX
   mov EDX,[EAX]
@Summ:
   add EAX,4
   add EDX,[EAX]
   loop @Summ
   mov EAX,EDX
end;

Добавили вот это:
Код: Выделить всё
dec ECX

Потому что эти поля отличаются на еденицу....
Вот это меня и возмутило, я думал что это Бага, но Sergei I. Gorelkin, говорит, что это принципиально так сделано (я предпологаю, что из за того, что считается мол процедура High используется наиболее часто).
Поэтому я не могу так писать, используя поле Length, для использования библиотеки как в FPC так и в Delphi. :( Вот это меня и растраивает :cry:
Выход, либо внутри блока Asm вызывать процедуру Length, чего не хотелось... :(
А использование директивы Delphi mode, ничего не меняет, FPC все равно генерит для поля Length не длину массива, а максимальный элемент тобишь High.

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 16.03.2011 08:18:38
devels
Ну может для fpc прибавлять +1:


Код: Выделить всё
mov eax,[eax]
mov ecx,[eax-4]
{$ifdef FPC}
add eax, 1
{$endif}

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 16.03.2011 08:48:11
Иван Шихалев
Maxizar писал(а):Но получается, что так нельзя делать, если данный код будет находится в ДЛЛ и работать с динамичискими массивами, которы будут создаваться в другом языке отличным от Free Pascal.


Именно нельзя. Другой язык вообще не обязан понимать, что такое динамический массив в object pascal-трактовке.

Re: Длина массива через указатель, различие в Delphi и FPC

СообщениеДобавлено: 16.03.2011 12:20:22
Maxizar
devels писал(а):Ну может для fpc прибавлять +1:

Нет, просто получается, что нужно либо иметь две разных DLL или две разных функции, для FPC и Delphi..

Иван Шихалев писал(а):Именно нельзя. Другой язык вообще не обязан понимать, что такое динамический массив в object pascal-трактовке.

Да, тут так именно и происходит, что не обязан. Просто я думал что динамические массивы, который сделает и Delphi и FPC, должны быть одинаковыми, теперь знаю, что нет.

Просто я думал, что поле Length, будет одинаковым, и изночально подумал не баг ли... но оказывается, что нет...

И на будущее, вот где для Free Pascal, написаны такие тонкости... или чтобы это понять, нужно сидеть с дебаггером или читать исходный код FPC? Или я опять не там искал :(

PS. Спасибо, вопрос буду считать решенным.