Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi For-Schleife von 0 bis 7 fängt bei 8 an (https://www.delphipraxis.net/35973-schleife-von-0-bis-7-faengt-bei-8-a.html)

Flogo 14. Dez 2004 11:31


For-Schleife von 0 bis 7 fängt bei 8 an
 
:freak: :wiejetzt:
Kann das sein? Der Schleifenkopf sieht so aus
Delphi-Quellcode:
    for i := 0 to 7 do
    begin
teste ich i im ersten Schleifendurchlauf mit der Maus (Haltepunkt -> Maus auf i -> Hint) bekomme ich immer [i = 8].
Schreibe ich ein
Delphi-Quellcode:
showmessage(IntToStr(i));
in die Schleife funktioniert alles so wie es soll
:wiejetzt:

jim_raynor 14. Dez 2004 11:36

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Wiederrum eine hervorrangende Optimierung des Codes durch den Delphi-Compiler:

siehe http://www.delphi-source.de/insider/...lvariablen.php

Es wird ausgenutzt, dass ein Vergleich gegen Null schneller und weniger speicher braucht. Daher wird in bestimmten Fällen die Variable Rückwärts gezählt.

Sharky 14. Dez 2004 11:36

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Jup,
das liegt daran der der Compiler den Code so optimiert das er möglichst effektiv arbeitet. Und in der regel ist es einfacher runter zu zählen. Wenn es für das Programm jedoch wichtig ist das "richtig" gezählt wird macht der Compiler keine Optimierung.

Flogo 14. Dez 2004 11:58

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Das ist ja schön :drunken: .
Naja wäre es, wenn es eben wirklich keinen Unterschied machen würde. Aber wenn ich ein Array[0..7] abfragen will nützt mir ein Index 8 eben nichts *grrr*
Gibt es vielleicht irgendeine Möglichkeit das zu umgehen?

Urmel 14. Dez 2004 12:02

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Flogo
...Aber wenn ich ein Array[0..7] abfragen will nützt mir ein Index 8 eben nichts ...

Dann sollte der Compiler das eigentlich von sich aus richtig machen. Wie oben schon gesagt wurde wir ja nur optimiert wenn es auf den Programm ablauf keinen Einfluss hat. Es kann ja sein das der Compiler dein Array "intern" als Array [1..8] behandelt.

Mario 14. Dez 2004 12:07

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Hast Du das mal ohne Optimierung getestet?

Hast Du ein schlüssiges Codebeispiel?

Flogo 14. Dez 2004 12:07

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Hey das ist eine Idee: Ich mache mein Array einfach auch extern zu einem Array[1..8]
Ist zwar irgendwie dämlich dass man so ausweichen muss aber bei mir machts keinen Unterschied.
Danke für die schnelle Hilfe

jim_raynor 14. Dez 2004 12:29

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Flogo
Hey das ist eine Idee: Ich mache mein Array einfach auch extern zu einem Array[1..8]
Ist zwar irgendwie dämlich dass man so ausweichen muss aber bei mir machts keinen Unterschied.
Danke für die schnelle Hilfe

Machs blos nicht. Der Compiler weiss schon was er tut. Wenn du immer einen Eintrag in der For-Schleife indizierst, legt er automatisch einen zusätzlichen Pointer ab, der schon richtig gesetzt wird. Mach dir deswegen keine Sorgen. Der Compiler macht das schon richtig. Sonst wären ja die For-Schleifen unnütz.

Luckie 14. Dez 2004 12:33

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Ich glaube, du hast nicht verstanden, was dir versucht wird zu erklären. In ASM baut man schleifen so auf, dass man die Zählvariable in ein Register packt, dieses immer dekrementiert und auf null prüft. Ein Register auf null prüfen ist einfacher und damit performanter als zwei von null verschiedenen Werte mit einander zu vergleichen.

So, wenn der Delphi Kompiler jetzt sieht, dass es unerheblich ist, wie rumm die Schleife läuft, dann optimiert er den Code so, dass oben beschriebener ASM bei rauskommt. Natürlcih hat das zur Folge, dass die Schleife rückwärts läuft. Da es keinen Einfluss auf den Code hat, ist es egal. So bald der Kompiler sieht, dass der Wert der Zählvariablen in der Schleife eine Rolle spilet, ein einfaches ShowMessage reicht da schon, optimiert er nicht mehr.

Warum also willst du an deinem Code was ändern? Wenn du nicht willst, dass dein Programm performant und speicherschonend läuft, dann schlate die Optimierung ab.

negaH 15. Dez 2004 05:58

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Ich glaube, du hast nicht verstanden, was dir versucht wird zu erklären. In ASM baut man schleifen so auf, dass man die Zählvariable in ein Register packt, dieses immer dekrementiert und auf null prüft. Ein Register auf null prüfen ist einfacher und damit performanter als zwei von null verschiedenen Werte mit einander zu vergleichen.
Um das noch genauer auszudrücken:

Bei jeder mathematischen Operation mit Ganzzahlen auf einer Intel CPU wird nicht nur das Resultat berechnet sondern auch Flags gesetzt die bestimmte Besonderheiten des Resultates wiederspiegeln. Wird also ein Register dekrementiert dann wird in den Flags das ZERO Flag automatisch durch diesen DEC Befehl gesetzt wenn das Resultat NULL wurde. D.h. also das solche Operationen implizit bestimmte Abfragen durchführen, und das sofort in diesem Befehl. Danach gibt es bedingte Sprungbefehle die IMMER diese Flags als Grundlage nehmen um zu detektieren ob sie verzweigen sollen. D.h. beim Dekrement eines Register's werden minimal 2 CPU Befehle nötig. Inkrementiert man aber bis zu einer Zahlengrenze so muß die CPU nach dem Inkrement das Resultat = Zählvariable mit dieser Zahl explizit vergleichen und dann erst springen. Das benötigt also 3 Befehle.

Die Optimierung des Compilers benötigt also weniger Befehle um das gleiche Ziel zu erreichen. Die dazu benutzen Befehle sind NICHT schneller als die Befehle des unoptimierten Codes, das ist ein Irrtum.

Willst du aus Debugtechnischen Gründen die Optimierung des Compilers unterdrücken dann schalte die Optimierung einfach aus beim Compilieren.

Gruß Hagen

Robert Marquardt 15. Dez 2004 07:41

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Der Wert kann durchaus gelegentlich bei 8 stehen.
Der Compiler beginnt bei 8 und dekrementiert erst mal und testet dann.

jbg 15. Dez 2004 07:55

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von negaH
Die dazu benutzen Befehle sind NICHT schneller als die Befehle des unoptimierten Codes, das ist ein Irrtum.

Ein CMP ist eine SUB Anweisung wo das Ergebnis verworfen und nicht in ein Register geschrieben wird. Und ich bin da schon der Meinung, wenn das Programm keine tzsätzliche Substraktion ausführen muss, dass es ein klein wenig schneller läuft. Und wenn dann die Zählervariable nicht in ein Register eingelagert werden kann, dann ist da auch noch ein Speicherzugriff mehr. Und bei einer Schleife, die mehrere Tausend male durchlaufen wird, macht sich das dann doch leicht bemerkbar.

Muetze1 15. Dez 2004 09:09

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Moin!

Zitat:

Zitat von negaH
Bei jeder mathematischen Operation mit Ganzzahlen auf einer Intel CPU wird ...

Auch bei x86 CPUs von VIA, Transmeta, AMD etc...

MfG
Muetze1

Flogo 15. Dez 2004 12:14

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Mein Problem war eben, dass ich ein i = 8 als Index in ein Array von 0 bis 7 einsetzen wollte
ungefähr so:
Delphi-Quellcode:
var
  A: array[0..7] of Integer;
  i: Integer;
begin
  for i := 0 to 7 do
    A[i] := 42;
end;
Als Ergebnis gabs ne AV bei i = 8. Als ich das Array auf [1..8] geändert hatte gings. So wie ich den Link von jim_raynor verstanden hab sollte das ja eigentlich nichts ausmachen :gruebel:
Naja vielleicht war auch schon vorher irgendwo ein kleiner Fehler, der nur dabei rausgekommen ist.

Mittlerweile hat sich das bei mir sowieso schon geklärt, da ich auf eine andere Suchmethode umgestiegen bin, in der das Array gar nicht mehr vorkommt.
War aber trotzdem mal ganz interessant zu wissen.

Die Muhkuh 15. Dez 2004 12:22

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Hi Flo,


der Code geht bei mir wunderbar. Ich bekomme keine AV oder so.

GuenterS 15. Dez 2004 12:44

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Flogo
Mein Problem war eben, dass ich ein i = 8 als Index in ein Array von 0 bis 7 einsetzen wollte
ungefähr so:
Delphi-Quellcode:
var
  A: array[0..7] of Integer;
  i: Integer;
begin
  for i := 0 to 7 do
    A[i] := 42;
end;
Als Ergebnis gabs ne AV bei i = 8. Als ich das Array auf [1..8] geändert hatte gings. So wie ich den Link von jim_raynor verstanden hab sollte das ja eigentlich nichts ausmachen :gruebel:
Naja vielleicht war auch schon vorher irgendwo ein kleiner Fehler, der nur dabei rausgekommen ist.

Mittlerweile hat sich das bei mir sowieso schon geklärt, da ich auf eine andere Suchmethode umgestiegen bin, in der das Array gar nicht mehr vorkommt.
War aber trotzdem mal ganz interessant zu wissen.


Ich kann mir eigentlich nicht ganz vorstellen, dass vor Dir noch niemand eine for Schleife mit einem Array eingesetzt hat, insofern würde ich den Fehler eher woanders suchen, nichtsdesto trotz brenne ich darauf das zuhause selbst mal zu versuchen. Was hast Du denn für eine Delphi Version verwendet?

Luckie 15. Dez 2004 12:54

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Flogo
Mein Problem war eben, dass ich ein i = 8 als Index in ein Array von 0 bis 7 einsetzen wollte
ungefähr so:
Delphi-Quellcode:
var
  A: array[0..7] of Integer;
  i: Integer;
begin
  for i := 0 to 7 do
    A[i] := 42;
end;
Als Ergebnis gabs ne AV bei i = 8.

Ist ja auch logisch oder? Den Index 8 gibt es ja gar nicht. Das achte Element hat bei dir den Index 7.

Sanchez 15. Dez 2004 13:00

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Luckie
Ist ja auch logisch oder? Den Index 8 gibt es ja gar nicht. Das achte Element hat bei dir den Index 7.

Aber bei dem Beispiel sollte i während eines Schleifendurchlaufes niemals den Wert 8 erreichen bzw. haben.

Luckie 15. Dez 2004 13:04

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Das ist wohl schon die korrigierte Fassung.

Hansa 15. Dez 2004 13:49

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Wenn man folgendes laufen läßt :

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var i : integer;
begin
  for i := 0 to 7 do
    showmessage ('*'+IntToStr (i)+'*');
  showmessage (IntToStr (i));
end;
dann hat i innerhalb der Schleife einen Wert von 0 bis 7. Außerhalb der Schleife, ganz am Schluß, ist es aber 8. Warum das so ist ? Mir egal. Nur wichtig für Nanosekunden-Programmierer. :zwinker: Man sollte eben nur den Schluß daraus ziehen, solche Variablen wirklich nur für den Zweck der Schleife einzusetzen.

Luckie 15. Dez 2004 13:52

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Nach einer for Schleife ist die Zählvariable undefiniert. Die Speicherstelle hat wohl nur zufällig den Wert 8. Der Compiler sollte eine entsprechende Warnung ausgegeben haben.

Hansa 15. Dez 2004 14:06

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Die Zählvariable hat immer einen um 1 erhöhten Wert außerhalb der Schleife. Das ist kein Zufall. Der Compiler meckert nur, wenn innerhalb der Schleife sogar noch ein Wert a la i := i + 1; zugewiesen wird. Wie gesagt, ich weiß auch nicht warum, aber es ist egal. Das müßte ein Compiler-Bauer sagen. Und dabei käme wohl eine Diskussion zustande, daß intern ein SHL oder sonstein ASM Befehl genutzt wird, aus Performancegründen und, daß der Compiler in die Knie gehen würde, falls man den Wert auf den letzten der in der Schleife tatsächlich genutzten Werte zurücksetzt und, daß das auch überflüssig ist, da die Variable ja sowieso auf die Schleife beschränkt bleiben soll. :mrgreen:

Neutral General 15. Dez 2004 14:10

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Hansa
Die Zählvariable hat immer einen um 1 erhöhten Wert außerhalb der Schleife. Das ist kein Zufall.

Würde mich ja mal interessieren warum das so ist :stupid:

jim_raynor 15. Dez 2004 14:17

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Neutral General
Zitat:

Zitat von Hansa
Die Zählvariable hat immer einen um 1 erhöhten Wert außerhalb der Schleife. Das ist kein Zufall.

Würde mich ja mal interessieren warum das so ist :stupid:

Ich glaube ehr, dass es nicht so ist. Wenn du die Zählvariable in der Schleife nicht benutzt, wird diese ja (wie wir in diesem Beitrag gelernt haben) Abwärts gezählt. Somit wirst du am Ende auf einen anderen Wert deiner Schleifen-Variable kommen. Logisch wäre dann, wenn in der Zählvariable ne 0 drin stehen würde, aber auch das ist wahrscheinlich nicht so, da zum Zählen nicht unbedingt die Variable sondern einfach nur ein CPU-Register benutzt wird. Deshalb kommt auch die besagte Warnung, dass der Wert nach einer Schleife undefiniert ist.

Luckie 15. Dez 2004 14:20

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Hansa
Der Compiler meckert nur, wenn innerhalb der Schleife sogar noch ein Wert a la i := i + 1; zugewiesen wird. Wie gesagt, ich weiß auch nicht warum, aber es ist egal.

Ich weiß nicht, was du für einen Compiler hast, aber meiner sagt mir:
Zitat:

[Warnung] Unit1.pas(31): FOR-Schleifenvariable 'i' kann nach Durchlauf undefiniert sein
:roll:

Und dass man innerhalb einer Schleife so nicht die Zählvariable manipulieren kann, ist allseits bekannt.

Chewie 15. Dez 2004 14:27

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Luckie
Und dass man innerhalb einer Schleife so nicht die Zählvariable manipulieren kann, ist allseits bekannt.

Naja, aber nicht bei C, Java, etc. Dort ist das schon möglich. In Pascal ist das, will man keine Warnung produzieren, nur über den Zeiger-Umweg möglich. Warum, würde mich auch interessieren, ich vermute aber aufgrund der mathematisch-logischen Orientierung von Pascal.


Zitat:

Zitat von Neutral General
Zitat:

Zitat von Hansa
Die Zählvariable hat immer einen um 1 erhöhten Wert außerhalb der Schleife. Das ist kein Zufall.

Würde mich ja mal interessieren warum das so ist :stupid:

Gehen wir mal von folgender (nicht-optimierten) for-Schleife aus:

Delphi-Quellcode:
for i := 0 to 7 do
begin
end;
Diese könntest du (unter Erzeugung des prinzipiell gleichen Maschinencodes) in folgende while-Schleife umwandeln:
Delphi-Quellcode:
i := 0;
while i <= 7 do
begin
  Inc(i);
end;
Ist jetzt klar, warum?

PRehders 15. Dez 2004 14:28

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Neutral General
Zitat:

Zitat von Hansa
Die Zählvariable hat immer einen um 1 erhöhten Wert außerhalb der Schleife. Das ist kein Zufall.

Würde mich ja mal interessieren warum das so ist :stupid:

Wenn man Optimierungen (wie die Umkehrung des Schleifendurchlaufs) mal beseite lässt, ist die Erklärung recht einfach: Da bei einer For-Schleife der untere Grenzewert ja vorab bereits höher sein kann als der obere (bei Verwendung von Variablen für diese Werte), wird eine For-Schleife immer als abweisende Schleife gebaut, also die Abbruchbedingung wird vorab geprüft.
Nach dem Schleifendurchlauf wird die Zählvariable erhöht und wieder an den Anfang (die Prüfung!) gesprungen. Daher muss die Zählvariable nach dem Ende der Schleife um eins höher sein als der Abbruchwert.

Die Optimierungen des Compilers können dies natürlich ändern, aber nur dann, wenn die Zählvariable entweder nicht oder in einer sog. inavarianten Weise benutzt werden: Wird ein Array mit konstanten oder nicht von der Zählvariablen abhängigen Werten gefüllt, kann der Compiler das Füllen auch umdrehen und ggf. nur ein Register benutzen, so dass der Wert nach dem Schleifendurchlauf überhaupt nicht mehr existiert.
Dies jedoch beruht alles auf der Forderung von Pascal, dass die Zählvariable bzw. der Inhalt nach dem Schleifendurchlauf nicht mehr definiert ist!

Viel Spaß noch

Peter

Flogo 15. Dez 2004 15:15

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Luckie
Das ist wohl schon die korrigierte Fassung.

Nicht direkt! Der Schleifenkopf ist der gleiche wie in meinem Programm und die Array-deklaration ist bis auf den Typ auch die gleiche (bei mir war es TPoint)
Dass der Code, den ich gepostet habe, läuft, will ich gar nicht bezweifeln. Nur kam eben bei mir eine AV.
Die Zuweisung war bei mir noch anders (eben ein Point) und es wurden noch ein paar andere Sachen, die nichts mit i oder dem Array zu tun hatten, in der Schleife erledigt.
Leider hab ich den fehlerhaften Code nicht mehr sonst würde ich auch den hier posten. Das vorhin war nur als Beispiel gedacht, damit ihr seht, wie mein Array und meine For-Schleife aufgebaut sind.
So wie ich die Diskussion hier lese (hätte nie gedacht, dass das so ein großes Thema wird :cyclops: ) denke ich, dass der Fehler bei mir gar nicht in der Schleife lag, sondern irgendwo vorher.
Denn auch wenn als Hint [i = 8] und [Array[i] = inaccessable Value] angezeigt wurden, müsste das intern so geregelt werden, dass trotzdem der richtige Wert benutzt wird (-> Link von jim)

jbg 15. Dez 2004 19:08

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von Chewie
Naja, aber nicht bei C, Java, etc. Dort ist das schon möglich. In Pascal ist das, will man keine Warnung produzieren, nur über den Zeiger-Umweg möglich.

Also bei Pascal geht das wunderbar. Nur Object Pascal und sein Nachfolger Delphi (Language) machen da nicht mit.

Unter TurboPascal konnte man noch folgende schöne, sehr lang dauernde Schleife programmieren:
Delphi-Quellcode:
for i := 0 to 2 do
  Inc(i);
Zitat:

Warum, würde mich auch interessieren, ich vermute aber aufgrund der mathematisch-logischen Orientierung von Pascal.
Der Hauptgrund wird wohl die Code-Optimierung gewesen sein.

negaH 17. Dez 2004 17:01

Re: For-Schleife von 0 bis 7 fängt bei 8 an
 
Zitat:

Zitat von jbg
Zitat:

Zitat von negaH
Die dazu benutzen Befehle sind NICHT schneller als die Befehle des unoptimierten Codes, das ist ein Irrtum.

Ein CMP ist eine SUB Anweisung wo das Ergebnis verworfen und nicht in ein Register geschrieben wird. Und ich bin da schon der Meinung, wenn das Programm keine tzsätzliche Substraktion ausführen muss, dass es ein klein wenig schneller läuft. Und wenn dann die Zählervariable nicht in ein Register eingelagert werden kann, dann ist da auch noch ein Speicherzugriff mehr. Und bei einer Schleife, die mehrere Tausend male durchlaufen wird, macht sich das dann doch leicht bemerkbar.


Du hast mich falsch verstanden :)

Ein zusätzlicher Befehl wie CMP kostet mehr Taktzyklen pro Schleife, trotzdem ist ein CMP genauso schnell wie ein SUB oder DEC. Zählt die Schleife gegen Null mit dem Befehl DEC so benötigt man nach dem DEC bei Überprüfung auf Null kein zusätzliches CMP mehr sondern kann die Flags direkt auswerten.
D.h. also die Zählweise gegen Null spart einen zusätzlichen Befehl. Die Befehle selber sind meistens gleich schnell ausgeführt !!

Gruß Hagen


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:44 Uhr.

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