Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Arrays (https://www.delphipraxis.net/11149-arrays.html)

Psycho 31. Okt 2003 21:20


Arrays
 
Hi!

Wie kommt es, dass ein array, der für 5 Werte deklariert ist, dennoch 7 oder mehr Werte speichert und auch wieder ausgibt?

Delphi-Quellcode:
var
        Ziehung:array[1..5] of integer;
        x,y:integer;


begin
randomize;
for x:=1 to 7 do
begin
ziehung[x]:=random(6)+1;
writeln(Ziehung[x]);
end;
readln;
  end.
Danke für jede Hilfe
Psycho

[edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit]

Chewie 31. Okt 2003 21:24

Re: Arrays
 
Wahrscheinlich ist die Bereichsüberprüfung deaktiviert. Das erhöht die Laufgeschwindigkeit, erhöht aber auch die Fehleranfälligkeit, da, wie du schon festgestellt hast, Arrays beliebig indiziert werden können und damit allozierter, aber nicht zum Array zugehöriger Speicher überschrieben werden kann!
Mit den Comilerschaltern $RANGECHECKS ON bzw. $RANGECHECKS OFF kannst du dieses Verhalten selbst beeinflussen.

Psycho 31. Okt 2003 23:49

Re: Arrays
 
Zunächst einmal vielen Dank, es funktioniert.

Trotzdem die Frage: Es wird doch ein entsprecvhender Speicherplatz reserviert. Wenn nun der Bereich überschritten wird, wo liegt der Speicherplatz für die zusätzlichen Werte und wie wird er verwaltet, er ist ja wieder abrufbar?

Es ist zwar ganz gut, wenn man dies Problem beseitigen kann, es erklärt mir aber nicht, was da Delphi mit den Speichern macht.

Gruß Psycho

Luckie 31. Okt 2003 23:51

Re: Arrays
 
Im schlimmesten Fall überschreibst du dir Speicher, der im Speicher nach dem Array liegt. Und wenn da Dten liegen, die dein Programm später braucht, kommt es zu einer AccessViolation.

Psycho 31. Okt 2003 23:58

Re: Arrays
 
Das kann ich grundsätzlich ja verstehen, aber beim Aufruf ( writeln (ziehung[7] ) steigt das Programm nicht aus, es findet genau den "richtigen " Wert. Ist das Glück, weil der Bereich zwischen dem Speichern und Auslesen nicht verwendet wurde, oder wird er von Delphi geschützt?

Luckie 1. Nov 2003 00:00

Re: Arrays
 
Glück.

Chewie 1. Nov 2003 09:25

Re: Arrays
 
Wenn du zwischen dem Zuweisen an einen Array-Index, der außerhalb des Indexbereichs liegt und dem Abrufen keinen Speicher in deinem Programm allozierst und auch vorher an dieser Stelle kein allozierter Speicher war, dann gibts auch keine Access-Violation. Und wenn auch kein anderer Prozess an die zugehörige physische Speicherstelle keine Daten geschrieben hat, steht noch das drin, was vorher drin war.
Mit anderen Worten: Je länger die zeitliche Differenz zwichen Schreiben und Lesen ist und je mehr Prozesse laufen, desto höher die Wahrscheinlichkeit, dass da nicht mehr das steht, was du hingeschrieben hast.

Luckie 1. Nov 2003 09:41

Re: Arrays
 
Zitat:

Zitat von Chewie
Und wenn auch kein anderer Prozess an die zugehörige physische Speicherstelle keine Daten geschrieben hat

Ein Prozess kann nicht auf den Adressraum eines anderen Prozesses zugreifen (unter 32 Bit Windows). Und mit physischen Speicher hat das gar nichts zu tun. Im Grunde genommen kennt ein Prozess keinen physischen Speicher. Es sieht nur seine 2 GB Adressen die ihm zur Verfügung stehen. Die Abbildung der Adresse aus dem Adressraum eines Prozess auf physischen Speicher nimmt das Betriebssystem bei Bedarf vor.

Chewie 1. Nov 2003 09:46

Re: Arrays
 
Richtig Luckie, aber:
Angenommen, Prozess 1 hat ein Array[1..5] und wird im Index 7 adressiert. Diese Speicherstelle ist von Prozess 1 nicht belegt und liegt im virtuellen Speicher eine Elementgröße hinter dem Array. Im physischen Speicher liegt es irgendwo.
Nun kommt Prozess 2 und alloziert irgendwo in seinem virtuellen Speicherraum Speicher. Dieser virtuelle Speicher wird nun auf den phyischen abgebildet, und das kann zufällig genau die Speicherstelle sein, an der oben genanntes Element gespeichert wird.

Das ist so ähnlich wie wenn du Variablen nicht initialisierst: Obwohl dein Programm das erste Mal läuft und noch nirgends Speicher freigegeben wurde, ist eine Variable nicht mit 0 intiialisiert, sondern mit dem, was vorher im physischen Speicher an der entsprechenden Adresse stand.

SirThornberry 1. Nov 2003 10:09

Re: Arrays
 
if Falle das der Speicher des ArrayIndex[7] nicht mehr zum process gehört kommt es beim schreibn zu einer AccessViolation. Es kann nur der Speicher der zum Process gehört ohne accessviolation gelesen und geschrieben werden. Sonst könnte man ja einfach mal so windows bissl durcheinander bringen indem man im speicherbereich vom Betriebssystem was ändert und sich somit irgendwelche zugriffsrechte holt

Chewie 1. Nov 2003 10:13

Re: Arrays
 
Aber der Speicher Array[7] gehört nur dann nicht mehr zum Speicherbereich, wenn Array[5] >= (2^31 - 1) - Größe eines Array-Elements.

choose 2. Nov 2003 20:14

Re: Arrays
 
Hey Psycho,

Daten in einem Delphi Programm werden zZt entweder im Datensegment (globale Variablen), auf dem Stack (lokale Variablen, Rücksprungadressen und zT Parameter) oder auf dem sog. Heap (Objekte und Daten mit New, Getmem) abgelegt. Diese drei Bereiche sind allerdings in ihrer Struktur recht ähnlich: In ihnen deklarierte Variablen liegen linear im jeweiligen Speicherbereich hintereinander weg.

Deklarierst Du zB die beiden Variablen
Delphi-Quellcode:
var
  iFoo: Integer;
  iBar: Integer;
als globale Variablen, liegt die iFoo zB bei der Adresse @iFoo=$0044FC24 und iBar bei @iBar=$0044FC28 also bei @iFoo+4, vier Bytes "weiter hinten" im Speicher, hier dem Datensegment. Die vier Bytes entsprechen der Größe eines Integers unter Delphi.

Betrachtest Du nun ein Array, liegt jede Zelle des Arrays linear hinter ihrer Vorgängerzelle im Speicher. Bei dem Array arFoobar
Delphi-Quellcode:
var
  arFoobar: array[0..1] of Integer;
verhält es sich demnach genauso wie bei den beiden Variablen iFoo und iBar: Die Adresse der Zelle arFoobar[1] liegt vier Bytes "weiter hinten" als arFoobar[0].

Deklarierst Du nun Variablen hinter diesem Array, zB eine neue Variable iBarfoo, ebenfalls vom Typ Integer, würde der Code
Delphi-Quellcode:
arFoobar[2]:= 137;
so übersetzt werden (dies ist in der Praxis nur mit einer Hilfsvariablen möglich, weil das Litaral 2 vom Compiler gegen die Arraygrenzen geprüft wird), dass die Adresse der Variablen iBarfoo berechnet und der Speicher an dieser Stelle manipuliert wird. In diesem Bsp ist also arFoobar[2] identisch mit iBarfoo!

Wenn Du Dir das Kompilat des Codes ansiehst, wirst Du erkennen, dass der Compiler lediglich Code generiert, der den Index (hier: 2) mit der Größe der Zelle des Arrays (hier: 4) multipliziert und zur Adresse der ersten Zelle des Arrays addiert (hier: @arFoobar=@arFoobar[0]).

Der Compiler kann die Größe des Datensegments zur Kompilierzeit berechnen und legt sie bei Delphiprogramm hart ins erstellte Binary ab. Das Betriebssystem kann deshalb ermitteln, ob ein Speicherzugriff gültig ist und löst andernfalls einen AccessVialation-Interrupt aus, der auf eine entspr. Delphi-Exception gemappt wird.
Der Heap eines Programms ist relativ dynamsch und wird unter Delphi mithilfe eines speziellen Speichermanagers verwaltet. Hier kannst Du Dich leider nicht darauf "verlassen", dass Du eine entsprechende Fehlermeldung bekommst, wenn Du bspw. einen Speicherbereich eines Objekts verwendest, das bereits freigegeben ist.
Der Stack besitzt bei Delphiprogrammen zwar eine feste größe, die ist allerdings so gewählt, das sie (hoffentlich) nie erreicht wird, andernfalls gäbe es einen StackOverflow. Stattdessen gibt es einen "aktuelle Bereich" (Stackframe, Stackpointer, Basepointer), an den geschrieben wird. An dieser Stelle ist das Schreiben über die zulässigen Grenzen hinaus sehr gefährlich, weil Du zB die mit obigen Code die Rücksprungadressen verändern würdest (der Stack ist TopDown organisiert)!

Allgemein lässt sich sagen, dass zwar interessante Dinge wie zB das lesen privater Exemplarvariablen, Vorinitialisieren von lokalen Variablen, Verbiegen von Rücksprungadressen, etc. mit dieser und ähnlichen Techniken möglich sind. Trotzdem geschehen solche Dinge idR eher unbewusst und können dann zu unerwünschten Erscheinungen führen. Darüber hinaus ist das bewusste Manipulieren von Daten stark vom Compiler abhängig (Data Word Size, Byte Alignment) und ist in einer VM (Managed Code in Delphi for .net) nicht möglich.

Stattdessen bietet der Compiler mit Range Checking {$R}, I/O Checking {$I} und Overflow Checking {$Q} brauchbare Hilfsmittel, um ungewolltes Verändern von Daten zu verhindern.

HTH


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:59 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz