Dynamische Array / Was passiert hier ?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Mathias
Beiträge: 6940
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Dynamische Array / Was passiert hier ?

Beitrag von Mathias »

Bae a2 := a wird nur der Zeiger auf die Array kopiert, das beweisen auch die beiden nachfolgenden Zeilen.
Später verkleinere ich a2 auf 5.
Somit müsste doch a auch Length 5 sein, aber es ist immer noch 10.

Ist da in meinem Test ein riesen Murks, oder passiert das sonst irgend was komisches ?

Code: Alles auswählen

type
  ta = array of byte;
 
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    procedure Test(a: ta);
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
var
  a: ta;
 
procedure TForm1.Test(a: ta);
var
  a2: ta;
  i: integer;
begin
  a2 := a;
  Form1.Memo1.Lines.Add(IntToStr(integer(Pointer(a))));
  Form1.Memo1.Lines.Add(IntToStr(integer(Pointer(a2))));
  for i := 0 to Length(a2) - 1 do begin
    Form1.Memo1.Lines.Add(IntToStr(a2[i]));
  end;
  SetLength(a2, 5);
  for i := 0 to Length(a2) - 1 do begin
    Form1.Memo1.Lines.Add(IntToStr(a2[i]));
  end;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  SetLength(a, 10);
  for i := 0 to Length(a) - 1 do begin
    a[i] := i;
  end;
  Memo1.Lines.Add('l a: ' + IntToStr(Length(a)));
  Test(a);
  Memo1.Lines.Add('l a: ' + IntToStr(Length(a)));
end;  
Ausgabe:

Code: Alles auswählen

l a: 10
-134342960
-134342960
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
l a: 10
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 2136
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Dynamische Array / Was passiert hier ?

Beitrag von Warf »

Das ist recht simpel, SetLength kannst du dir (ungefähr) so vorstellen (bei dem Beispiel int array):

Code: Alles auswählen

procedure SetLength(var a: TIntArr; const len: Integer);
var tmp: TIntArr;
begin
  GetMem(tmp, len*SizeOf(Integer));
  Move(a[0], tmp[0], min(len, Length(a));
  a := tmp;
end;
Das heißt, es wir ein neuer Array erstellt mit neuer größe, dann werden die Werte kopiert, und dann der alte Array (bzw die Adresse) mit dem neuen Überschrieben.

Da a2 immer noch auf den Alten array zeigt ist die Referenzenzahl noch nicht auf 0 gesunken, und damit hält a den neuen array Zeiger, a2 aber immer noch den alten

Mathias
Beiträge: 6940
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamische Array / Was passiert hier ?

Beitrag von Mathias »

Code: Alles auswählen

GetMem(tmp, len*SizeOf(Integer));
Hiermit erzeugst du einen neune Speicherbereich für die 2. Array.
Aber ich kopiere mit a2 := a; nur den Zeiger.

Oder würde dort noch ein FreeMem hingehören.

Code: Alles auswählen

procedure SetLength(var a: TIntArr; const len: Integer);
var tmp: TIntArr;
begin
  GetMem(tmp, len*SizeOf(Integer));
  Move(a[0], tmp[0], min(len, Length(a));
  FreeMem(a, len*SizeOf(Integer));
  a := tmp;
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

TBug
Beiträge: 179
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: Dynamische Array / Was passiert hier ?

Beitrag von TBug »

Mathias hat geschrieben:Aber ich kopiere mit a2 := a; nur den Zeiger.
Aber Du benutzt SetLength und da wird für Dein a2 ein neues Array erstellt.

Gib einfach nach SetLength()

Code: Alles auswählen

  Form1.Memo1.Lines.Add(IntToStr(integer(Pointer(a2))));
ein und Du wirst sehen, dass die Pointer auf a2 und a nicht mehr identisch sind.

Warf
Beiträge: 2136
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Dynamische Array / Was passiert hier ?

Beitrag von Warf »

Mathias hat geschrieben:

Code: Alles auswählen

GetMem(tmp, len*SizeOf(Integer));
Hiermit erzeugst du einen neune Speicherbereich für die 2. Array.
Aber ich kopiere mit a2 := a; nur den Zeiger.

Oder würde dort noch ein FreeMem hingehören.

Code: Alles auswählen

procedure SetLength(var a: TIntArr; const len: Integer);
var tmp: TIntArr;
begin
  GetMem(tmp, len*SizeOf(Integer));
  Move(a[0], tmp[0], min(len, Length(a));
  FreeMem(a, len*SizeOf(Integer));
  a := tmp;
end;
Mein Ziel war es nicht eine Korrekte Prozedur zu schreiben, ich wollte es nur veranschaulichen. Dynamische Arrays in Pascal sind Referenzgezählt weswegen mein Beispiel eine extrem obersimplifizierte version darstellet, und Praktisch so auch nicht funktioniert. Mit dem Freemem zum Beispiel würde dir der Zugriff auf a dann um die Ohren fliegen.

Wenn es dich genau interessiert, die Definition eines Dynamischen Arrays lässt sich in der dynarr.inc nachlesen und lautet:

Code: Alles auswählen

 
   pdynarray = ^tdynarray;
   tdynarray = packed record
      refcount : ptrint;
      high : tdynarrayindex;
   end;  
Der Erstellung ist dann etwa so (Wieder obersimplifizierter Pseudocode):

Code: Alles auswählen

  GetMem(tmp, SizeOf(tdynarray) + ElementCount*TypeSize);
  pdynarray(tmp)^.refcount := 1;
  pdynarray(tmp)^.high := ElementCount*TypeSize;
  inc(tmp, SizeOf(tdynarray));
  a := tmp;
Der Zuweisungsoperator (:=) ist dann etwa so etwas:

Code: Alles auswählen

  if assigned(a1) then
  begin
    dec(pdynarray(IntPtr(a1)-SizeOf(tdynarray))^.refcount); // alte referenz reduzieren
    if pdynarray(IntPtr(a1)-SizeOf(tdynarray))^.refcount = 0 then  // alten array free-en falls referenzcounter = 0
    begin
      dec(a1, sizeOf(tdynarray));
      Freemem(a1, sizeOf(tdynarray)+ pdynarray(a1)^.heigh);
    end;
  end;
  Move(a2, a1, SizeOf(Pointer)); // zuweisen
  inc(pdynarray(IntPtr(a1)-SizeOf(tdynarray))^.refcount); // neuen ref counter erhöhen
Aber das ist wie gesagt alles nur so pseudocode, der auch zum einen nicht ganz korrekt ist, zum anderen aber auch extrem vereinfacht ist, die genaue SetLength Prozedur aus der dynarr.inc ist wie folgt:

Code: Alles auswählen

procedure fpc_dynarray_setlength(var p : pointer;pti : pointer;
  dimcount : dword;dims : pdynarrayindex);[Public,Alias:'FPC_DYNARR_SETLENGTH']; compilerproc;
 
  var
     i : tdynarrayindex;
     movelen,
     size : sizeint;
     { contains the "fixed" pointers where the refcount }
     { and high are at positive offsets                 }
     realp,newp : pdynarray;
     ti : pdynarraytypeinfo;
     updatep: boolean;
     elesize : sizeint;
     eletype : pdynarraytypeinfo;
 
  begin
     ti:=pdynarraytypeinfo(pti);
 
     { skip kind and name }
     inc(pointer(ti),ord(pdynarraytypeinfo(ti)^.namelen)+2);
 
     ti:=aligntoptr(ti);
 
     elesize:=psizeint(ti)^;
     eletype:=pdynarraytypeinfo(pointer(pdynarraytypeinfo(pointer(ti)+sizeof(sizeint)))^);
 
     { determine new memory size }
     { dims[dimcount-1] because the dimensions are in reverse order! (JM) }
     size:=elesize*dims[dimcount-1]+sizeof(tdynarray);
     updatep := false;
 
     { not assigned yet? }
     if not(assigned(p)) then
       begin
          if dims[dimcount-1]<0 then
            HandleErrorFrame(201,get_frame);
          { do we have to allocate memory? }
          if dims[dimcount-1] = 0 then
            exit;
          getmem(newp,size);
          fillchar(newp^,size,0);
          updatep := true;
       end
     else
       begin
          realp:=pdynarray(p-sizeof(tdynarray));
          newp := realp;
 
          { if the new dimension is 0, we've to release all data }
          if dims[dimcount-1]<=0 then
            begin
               if dims[dimcount-1]<0 then
                 HandleErrorFrame(201,get_frame);
               if declocked(realp^.refcount) then
                 fpc_dynarray_clear_internal(realp,pdynarraytypeinfo(pti));
               p:=nil;
               exit;
            end;
 
          if realp^.refcount<>1 then
            begin
               updatep := true;
               { make an unique copy }
               getmem(newp,size);
               fillchar(newp^,size,0);
               if realp^.high < dims[dimcount-1] then
                 movelen := realp^.high+1
               else
                 movelen := dims[dimcount-1];
               move(p^,(pointer(newp)+sizeof(tdynarray))^,elesize*movelen);
 
               { increment ref. count of members }
               for i:= 0 to movelen-1 do
                 int_addref(pointer(newp)+sizeof(tdynarray)+elesize*i,eletype);
 
               { a declock(ref. count) isn't enough here }
               { it could be that the in MT environments  }
               { in the mean time the refcount was       }
               { decremented                             }
 
               { it is, because it doesn't really matter }
               { if the array is now removed             }
               { fpc_dynarray_decr_ref(p,ti); }
               if declocked(realp^.refcount) then
                 fpc_dynarray_clear_internal(realp,pdynarraytypeinfo(ti));
            end
          else if dims[dimcount-1]<>realp^.high+1 then
            begin
               { range checking is quite difficult ...  }
               { if size overflows then it is less than }
               { the values it was calculated from      }
               if (size<sizeof(tdynarray)) or
                 ((elesize>0) and (size<elesize)) then
                 HandleErrorFrame(201,get_frame);
 
               { resize? }
               { here, realp^.refcount has to be one, otherwise the previous }
               { if-statement would have been taken. Or is this also for MT  }
               { code? (JM)                                                  }
               if realp^.refcount=1 then
                 begin
                    { shrink the array? }
                    if dims[dimcount-1]<realp^.high+1 then
                      begin
                          int_finalizearray(pointer(realp)+sizeof(tdynarray)+
                            elesize*dims[dimcount-1],
                            eletype,realp^.high-dims[dimcount-1]+1);
                         reallocmem(realp,size);
                      end
                    else if dims[dimcount-1]>realp^.high+1 then
                      begin
                         reallocmem(realp,size);
                         fillchar((pointer(realp)+sizeof(tdynarray)+elesize*(realp^.high+1))^,
                           (dims[dimcount-1]-realp^.high-1)*elesize,0);
                      end;
                    newp := realp;
                    updatep := true;
                 end;
            end;
       end;
    { handle nested arrays }
    if dimcount>1 then
      begin
         for i:=0 to dims[dimcount-1]-1 do
           int_dynarray_setlength(pointer((pointer(newp)+sizeof(tdynarray)+i*elesize)^),
             eletype,dimcount-1,dims);
      end;
     if updatep then
       begin
         p:=pointer(newp)+sizeof(tdynarray);
         newp^.refcount:=1;
         newp^.high:=dims[dimcount-1]-1;
       end;
  end;                 

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

Warf hat geschrieben: Da a2 immer noch auf den Alten array zeigt ist die Referenzenzahl noch nicht auf 0 gesunken, und damit hält a den neuen array Zeiger, a2 aber immer noch den alten
Scheint logisch zu sein.

Allerdings heißt das, dass die Verwendung von so gebauten dynamischen Arrays extrem schlecht durchschaubar und damit fehlerträchig ist, weil man nicht weiß ob zwei Variablen nun dasselbe oder auf unterschiedliche Daten meinen.

Ich teste mal, ob das in Delphi genauso ist....

Tatsächlich Da haben sich die Sprachdesigner aber ganz schön verflogen.

Code: Alles auswählen

 
procedure TForm18.FormCreate(Sender: TObject);
var
  a, b: array of byte;
begin
  SetLength(a, 3);
  a[0] := 0;
  a[1] := 1;
  a[2] := 2;
  b := a;
  writeab(a,b);
 
  b[0] := 10;
  b[1] := 11;
  b[2] := 12;
  writeab(a,b);
 
  a[0] := 0;
  a[1] := 1;
  a[2] := 2;
  setlength(b,4);
  b[0] := 10;
  b[1] := 11;
  b[2] := 12;
  writeab(a,b);
 
 
end;
 
procedure TForm18.writeab(const a, b: array of byte);
begin
  Memo1.Lines.Add('a ' + IntToStr(Length(a)) + ':');
  Memo1.Lines.Add(IntToStr(a[0]));
  Memo1.Lines.Add(IntToStr(a[1]));
  Memo1.Lines.Add(IntToStr(a[2]));
  Memo1.Lines.Add('b ' + IntToStr(Length(b)) + ':');
  Memo1.Lines.Add(IntToStr(b[0]));
  Memo1.Lines.Add(IntToStr(b[1]));
  Memo1.Lines.Add(IntToStr(b[2]));
  Memo1.Lines.Add('----------');
end;
 
 
Ergebnis:
 
a 3:
0
1
2
b 3:
0
1
2
----------
a 3:
10
11
12
b 3:
10
11
12
----------
a 3:
0
1
2
b 4:
10
11
12
----------
 
 
 
-Michael

TBug
Beiträge: 179
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: Dynamische Array / Was passiert hier ?

Beitrag von TBug »

Warf hat geschrieben:Da a2 immer noch auf den Alten array zeigt ist die Referenzenzahl noch nicht auf 0 gesunken, und damit hält a den neuen array Zeiger, a2 aber immer noch den alten
Wenn Du die Speicherbereiche nach dem Einsatz von

Code: Alles auswählen

SetLength(a2, 5);
meinst, dann ist dies nicht ganz richtig, denn nicht a erhält den neuen Zeiger, sondern a2. Und deshalb bleibt a vollkommen unberührt.

Warf
Beiträge: 2136
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Dynamische Array / Was passiert hier ?

Beitrag von Warf »

mschnell hat geschrieben:
Warf hat geschrieben: Da a2 immer noch auf den Alten array zeigt ist die Referenzenzahl noch nicht auf 0 gesunken, und damit hält a den neuen array Zeiger, a2 aber immer noch den alten
Scheint logisch zu sein.

Allerdings heißt das, dass die Verwendung von so gebauten dynamischen Arrays extrem schlecht durchschaubar und damit fehlerträchig ist, weil man nicht weiß ob zwei Variablen nun dasselbe oder auf unterschiedliche Daten meinen.

Ich teste mal, ob das in Delphi genauso ist....

Tatsächlich Da haben sich die Sprachdesigner aber ganz schön verflogen.

-Michael

Nun ich finde genau das Gegenteil ist der Fall, es passiert genau das was man erwarten würde, das einzige wobei die Sprachdesigner einen Fehler gemacht haben ist vielleicht die Namensgebung der Funktion, da SetLength nicht impliziert dass es einen neuen Array erstellt (was aber alles in allem eigentlich die Funktion von Setlength ist).

Das Hauptproblem wenn man versuchen würde das so zu lösen wie du es für sinnvoll hältst ist, dass man entweder einen weitere Datenstruktur wie eine Linked List o.ä zum Referenzzähler hinzufügen müsste, in die man alle Referenzen einträgt, und diese bei SetLength durchgeht, was in Linearer Zeit- und Speicherkomplexität ausarten würde, oder man löst es mit einem Zeiger auf den ArrayPointer, was was man so eigentlich nicht in der Sprache implementieren muss, da der Entwickler für den die Aktuelle Lösung ein Problem ist kann das einfach selbst so lösen und es ist für dich nur extrem geringer aufwand

Das was du bemängelst wirst du auch genauso in C++ und anderen Referenzgezählten Sprachen finden.

Wenn dich das wirklich so sehr stört nutze die Klasse TList, das wäre so ziemlich die bequemste Implementierung für das was ich oben als: Zeiger auf den ArrayPointer beschrieben habe

PS: Man könnte auch in der Lazarus CCR Dokumentation von SetLength schreiben das SetLength einen neuen Array erstellt, und die Daten falls ein alter Array existiert so weit wie möglich in das neue Array kopiert

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

Warf hat geschrieben: es passiert genau das was man erwarten würde,
Naja.....

Code: Alles auswählen

 
type bytearray = array of byte;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
    procedure writeab(const a, b: bytearray);
  public
    { public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
 
procedure do_some_stuff(var b: bytearray; i: Integer);
begin
  if i <> 0 then SetLength(b,4);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
  var
    a, b: bytearray;
  begin
    SetLength(a, 3);
    a[0] := 0;
    a[1] := 1;
    a[2] := 2;
    b := a;
    do_some_stuff(b, 0);
    b[0] := 10;
    b[1] := 11;
    b[2] := 12;
    writeab(a,b);
 
    a[0] := 0;
    a[1] := 1;
    a[2] := 2;
    b := a;
    do_some_stuff(b, 4);
    b[0] := 10;
    b[1] := 11;
    b[2] := 12;
    writeab(a,b);
  end;
 
procedure TForm1.writeab(const a, b: bytearray);
begin
  Memo1.Lines.Add('a ' + IntToStr(Length(a)) + ':');
  Memo1.Lines.Add(IntToStr(a[0]));
  Memo1.Lines.Add(IntToStr(a[1]));
  Memo1.Lines.Add(IntToStr(a[2]));
  Memo1.Lines.Add('b ' + IntToStr(Length(b)) + ':');
  Memo1.Lines.Add(IntToStr(b[0]));
  Memo1.Lines.Add(IntToStr(b[1]));
  Memo1.Lines.Add(IntToStr(b[2]));
  Memo1.Lines.Add('----------');
end; 
führt auch zu dem Ergebnis:

Code: Alles auswählen

a 3:
10
11
12
b 3:
10
11
12
----------
a 3:
0
1
2
b 4:
1011
12
----------
 
Ich finde, man kann niemandem als wünschenswert verkaufen, dass der Aufruf einer nicht-internen Funktion mit einer lokalen Variablen, das Verhalten einer anderen lokalen Variable verändert.

Mit dem "Hauptproblem:" hast Du natürlich recht, dass die Performance leiden würde. Aber es geht:bei Strings tritt das Problem nicht auf.

Witzig ist auch, dass in der obigen Funtion do_some_stuff() "SetLength(b)" zu der völlig unverstehbaren Fehlermeldung "incompatible Type" führt, wenn der Parameter mit "array of byte" statt mit "bytearray" spezifiziert ist, was laut Type Definition ja genau dasselbe ist.

SetLength() scheint tatsächlich sogar dann ein neues Array zu machen, wenn die Länge nicht geändert wird.

-Michael

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Dynamische Array / Was passiert hier ?

Beitrag von mse »

mschnell hat geschrieben: Mit dem "Hauptproblem:" hast Du natürlich recht, dass die Performance leiden würde. Aber es geht:bei Strings tritt das Problem nicht auf.
Copy on write bei Zugriffen auf String Elemente ist eine derartige Bremse, dass ich wenn es auf Schnelligkeit ankommt statt

Code: Alles auswählen

 
 stringvar[n]:= charactervalue;
 

Code: Alles auswählen

 
 pchar(pointer(stringvar))[n]:= charactervalue;
 
verwende oder überhaupt mit pchar arbeite.
Witzig ist auch, dass in der obigen Funtion do_some_stuff() "SetLength(b)" zu der völlig unverstehbaren Fehlermeldung "incompatible Type" führt, wenn der Parameter mit "array of byte" statt mit "bytearray" spezifiziert ist, was laut Type Definition ja genau dasselbe ist.
Nein, ein Prozedurparameter "array of byte" ist ein offener Array Parameter
http://www.freepascal.org/docs-html/ref ... 700014.4.5
und kein dynamisches Array.
SetLength() scheint tatsächlich sogar dann ein neues Array zu machen, wenn die Länge nicht geändert wird.
Die Dokumentation ist hier:
http://www.freepascal.org/docs-html/ref ... 480003.3.1

Warf
Beiträge: 2136
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Dynamische Array / Was passiert hier ?

Beitrag von Warf »

mschnell hat geschrieben:Mit dem "Hauptproblem:" hast Du natürlich recht, dass die Performance leiden würde. Aber es geht:bei Strings tritt das Problem nicht auf.

Witzig ist auch, dass in der obigen Funtion do_some_stuff() "SetLength(b)" zu der völlig unverstehbaren Fehlermeldung "incompatible Type" führt, wenn der Parameter mit "array of byte" statt mit "bytearray" spezifiziert ist, was laut Type Definition ja genau dasselbe ist.

SetLength() scheint tatsächlich sogar dann ein neues Array zu machen, wenn die Länge nicht geändert wird.

-Michael
Strings nutzen wie mse bereits geschrieben hat Copy on Write, das heißt, der String wird erst Kopiert sobald ein Schreibender zugriff drauf stattfindet. Und das funktioniert vor allem, da strings öfter gelesen als verändert werden, da Strings hauptsächlich verwendet werden um Daten für User erkennbar auszugeben und weniger (eigentlich gar nicht) für die Datenverarbeitung. Darum wird ein String hauptsächlich anderen Variablen übergeben, um gelesen zu werden, was natürlich copy on Write relativ effizient macht. In der Datenverarbeitung ist das mit strings auch herzlich beschissen.

Zu deinem zweiten Punkt siehe den Beitrag von mse.

Und wie gesagt, SetLength macht nichts anderes als einen neuen Array zu erstellen, die Daten zu kopieren, und dann den referenzcounter des alten Arrays zu reduzieren, und entsprechend vielleicht auch den Speicher freizugeben, es finden dabei keine Überprüfungen auf aktuelle länge oder Inhalt des Arrays, oder Zeiger auf diesen Array statt, auch wenn der name der Prozedur in diesem Punkt recht verwirrend ist.
Ich finde, man kann niemandem als wünschenswert verkaufen, dass der Aufruf einer nicht-internen Funktion mit einer lokalen Variablen, das Verhalten einer anderen lokalen Variable verändert.
Dann müsstest du das gesamte Zeigersystem und die Referenzsemantik infrage stellen, denn dabei hat man genau das selbe Problem.

Die Referenzsemantik der Zeiger ist nunmal ein Paradigma mit eigenen Problemstellungen, und Eigenheiten die man einfach lernen muss, so wie man z.B. bei einem Integer durch einen Überlauf mit X = X + 5 bei X = 2^31-1 plötzlich -2^31 + 5 raus bekommt, kann man mit einem Pointer einfach werte mehrerer Variablen verändern, das ist nun mal eine Sache die man wissen muss, und mit der man lernen muss umzugehen, das ist einfach so

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

mse hat geschrieben:Nein, ein Prozedurparameter "array of byte" ist ein offener Array Parameter
Stimmt natürlich :oops:

Aber dieselbe Syntax mit verschiedener Bedeutung dient auch nicht gerade der Klarheit der Sprache.

Und dass ich um ein dynamisches Array als Parameter zu definieren eine zusätzliche Type Definition verwenden muss ist nicht wirklich störend, aber verwirrend.

-Michael
Zuletzt geändert von mschnell am Mi 9. Sep 2015, 10:05, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

Warf hat geschrieben:... weniger (eigentlich gar nicht) für die Datenverarbeitung.
Wenn man Übertragungs-Prozeduren programmiert sind Strings - gerade wegen der beschriebenen Eigenschaft, dass die Daten per "lazy copy" automatisch genau dann kopiert werden, wenn es notwendig ist - ausgesprochen praktisch und effektiv. Dass dabei ein gewisser Overhead entsteht stört in den meisten Fällen nicht.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

Warf hat geschrieben:
mschnell hat geschrieben:
Dann müsstest du das gesamte Zeigersystem und die Referenzsemantik infrage stellen, denn dabei hat man genau das selbe Problem.
Wenn man einen Zeiger an ein Unterprogramm übergibt, ändert das normalerweise nicht das spätere Verhalten von Datenstrukturen, auf die ein anderer Zeiger zeigt. (Außer natürlich der User programmiert das selber explizit in seinem Code, dann weiß er aber, was er tut). Dass die Werte, auf die verschiedene Zeiger zeigen durch beide verändert werden, ist jedem klar, aber das ist ja nicht das, worum es im Beispiel geht.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Array / Was passiert hier ?

Beitrag von mschnell »

mse hat geschrieben:Die Dokumentation ist hier:http://www.freepascal.org/docs-html/ref/refsu18.html#x42-480003.3.1
OK: "The SetLength call will make sure the reference count of the returned array is 1"

Das wusste ich nicht. Der Nebeneffekt Ist - finde ich - auch nicht wirklich offensichtlich und vermutlich in den meisten Einsatz-Fällen auch nicht wirklich sinnvoll.

Ich hätte (aus Performance-Gründen) vermutet, SetLength macht nur irgendetwas, wenn die ursprüngliche Länge kleiner ist (und setzt dann die allokierte Größe vorsichtshalber deutlich größer, z.B. auf die nächste Zweierpotenz) oder wenn die neue Größe wesentlich kleiner ist. Bessere Performance, aber keine Garantie für Refcount = 1.

-Michael

Antworten