Ich vermisse das nicht. Der Startindex ist immer der niedrigste Ordinalwert des Index-Datentyps. In VBA hatte mich der Code eines Kollegen einmal so verwirrt, dass bis heute das erste Element des Arrays einfach nicht ausgegeben wirdm.fuchs hat geschrieben:Was ich eher schmerzlich vermisse ist die Möglichkeit bei einem dynamischen Array den Startindex zu setzen.
Schleifen --> verwirrt
-
Socke
- Lazarusforum e. V.
- Beiträge: 3178
- Registriert: Di 22. Jul 2008, 19:27
- OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
- CPU-Target: 32bit x86 armhf
- Wohnort: Köln
- Kontaktdaten:
Re: Schleifen --> verwirrt
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
-
Warf
- Beiträge: 2216
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Schleifen --> verwirrt
Kann aber auch zu vielen Missverständnissen führen. Einfaches Beispiel. Mehrere Leute arbeiten an einem Pascal Projekt. Frisch von der Uni hat Alice noch nie vorher Pascal verwendet und ist nur etwas bewandert in Java, Bob hingegen ist ein alt eingefuchster Pascal Profi.mischi hat geschrieben:Es hindert dich doch nichts daran, deine Arrays mit 0 .. n-1 zu deklarieren. Das ist doch nur etwas mehr Schreibarbeit bei der Deklaration. Die Flexibilität von Pascal Arrays hat einige Vorteile. In Fortran beginnen alle Arrays mit dem Index 1 und das ist in einigen Fällen praktischer. Durch seine Flexibilität kann ich also in Pascal Code aus C UND Fortran direkt übernehmen, ohne dass ich etwas mit den Laufvariablen und Grenzen machen muss. Manchmal kommen einem auch Real-World Probleme auf den Tisch, bei denen weder bei 0 noch bei 1 angefangen wird zu zählen, sondern bei irgendeiner anderen Zahl. Ich habe immer wieder die Erfahrung gemacht, dass es überraschend häufig zu Fehlern kommt, wenn man das umsetzen muss, obwohl man denkt, dass es doch nicht so schwierig sein kann, eine Eins oder einen anderen Wert abzuziehen.Warf hat geschrieben:Über ranges kann man nunmal auch statische arrays definieren, daher hast du ein 26*26 Felder 2D array. Ein wenig verwirrend, vor allem da ich kein fan der range array definierung bin. Ich finde das C system mit nur anzahl an elementen dafür immer 0 basiert deutlich besser
Das Projekt selbst ist ein etwas länger bestehendes größeres program an dem neben Alice und Bob mehrere Entwickler dran sitzen, sagen wir einfach mal 5000 Lines of Code welches und einige statische Arrays drin hat. Alice, wie es die meisten Anfänger die von Java kommen so machen, iteriert über statische wie dynamische Arrays mit for i:=0 to Length(array)-1 (da iht schlicht weg die Übung mit Low und High fehlt).
Jetzt ändert Bob irgendwo das untere limit eines statischen Arrays auf 1, da die neue Datenstruktur die ID's mit 1 beginnt und damit der Array Index der ObjektID gleich sein soll (was ja grundsätzlich eine ausgezeichnete Idee ist).
Bob commited diese Änderung ins git, merged den spaß dann mit der Arbeit der letzten zwei Wochen und plötzlich funktioniert gar nichts mehr wie erwartet. Jetzt heißt es 2 Wochen an code changes von verschiedenen Entwicklern durchgehen um zu finden wo dieser Fehler aufgetreten ist. Also mit mindestens 5 stunden Arbeitszeit sollte man dafür schon rechnen.
So ähnlich ist es mir mal passiert, 1,5 Tage sind mir damals flöten gegangen nur um den Fehler zu finden. Man bekommt ja auch keine Fehlermeldung wenn man mal eben neben ein statisches array schreibt (Und range Checks musste ich deaktivieren da mir die dauernd false positives geworfen haben).
Daher fange ich bei statischen array ausnahmslos immer mit 0 an, denn selbst erfahrenen Pascal Programmierern passiert es auch mal das man for i:=0 to length()-1 statt high und Low macht
PS: Fortran ist außerdem eine komplette nieschensprache, die kaum noch wer außerhalb von Physiklaboratorien verwendet. Also so irrelevant das jemand der heute programmieren lernt wahrscheinlich nie eine Zeile Fortran sehen wird (außer er ist Physik student), und das eine mal das das in der Lebzeit eines Programmierers mal passiert kann man auch ein -1 hinschreiben
Zuletzt geändert von Warf am Di 10. Apr 2018, 18:38, insgesamt 1-mal geändert.
-
Mathias
- Beiträge: 7069
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Schleifen --> verwirrt
Wen es irgendwie geht, fange ich auch immer bei '0' an. high und low verwende ich nie.Daher fange ich bei statischen array ausnahmslos immer mit 0 an, denn selbst erfahrenen Pascal Programmierern passiert es auch mal das man for i:=0 to length()-1 statt high und Low macht
Da bekommt man sonst lustige Sachen, wie das Beispiel:
Code: Alles auswählen
var
sa: array[10..20] of byte;
procedure Ausgabe(a: array of byte);
var
i: integer;
begin
for i := 0 to Length(a) - 1 do begin
WriteLn(a[i]);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 10 to 20 do begin
sa[i] := i;
end;
Ausgabe(sa);
end; Code: Alles auswählen
var
sa: array[10] of byte;Eine besondere Array gibt es in Pascal, den String.
Der fängt bei 1 an. Aber dies wird eine Altlast von Turbo-Pascal sein, bei dem das 0. Zeichen die Länge angibt. Oder den heutigen FPC ShortString.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
Warf
- Beiträge: 2216
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Schleifen --> verwirrt
Lustig wird's erst wenn du auf den array out of Bounds schreibst. Wenn dein array eine globale Variable ist und im data teil liegt, kann es gerne passieren das du andere globale variablen überschreibst. Wenn du dann plötzlich den Pointer zu einer deiner Forms überschrieben hast wird es richtig lustig, wenn dir Form1.ShowModal() aus einer komplett anderen unit plötzlich eine SegFault wirft und du keine Ahnung hast woher das kommtMathias hat geschrieben:Wen es irgendwie geht, fange ich auch immer bei '0' an. high und low verwende ich nie.Daher fange ich bei statischen array ausnahmslos immer mit 0 an, denn selbst erfahrenen Pascal Programmierern passiert es auch mal das man for i:=0 to length()-1 statt high und Low macht
Da bekommt man sonst lustige Sachen, wie das Beispiel:Da hat C++ ausnahmsweise einen Vorteil, da kann man die Array in etwa so deklarieren:Code: Alles auswählen
var sa: array[10..20] of byte; procedure Ausgabe(a: array of byte); var i: integer; begin for i := 0 to Length(a) - 1 do begin WriteLn(a[i]); end; end; procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin for i := 10 to 20 do begin sa[i] := i; end; Ausgabe(sa); end;Da muss man nicht immer 0.. schreiben.Code: Alles auswählen
var sa: array[10] of byte;
Eine besondere Array gibt es in Pascal, den String.
Der fängt bei 1 an. Aber dies wird eine Altlast von Turbo-Pascal sein, bei dem das 0. Zeichen die Länge angibt. Oder den heutigen FPC ShortString.
Und das mit den Strings wird aktuell zum glück über die Typhelper geändert. Wenn man statt Str[Index] Str.Chars[Index] verwendet ist es null basiert (sowie alle type helper Funktionen von string). Lustig dabei wird es aber wenn man Str.Substring (0 basiert) und Pos (1 basiert) o.ä. verknüpft. Daher verwende ich aktuell auch fast ausschließlich die Typhelper, da die 0 basiert sind und ich da nicht extra drüber nachdenken muss
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2855
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Re: Schleifen --> verwirrt
Klingt nach schlechter Architektur, fehlender Kapselung und ungenügenden Unittests.Warf hat geschrieben:Einfaches Beispiel. Mehrere Leute arbeiten an einem Pascal Projekt.
[...]
Jetzt ändert Bob irgendwo das untere limit eines statischen Arrays auf 1, da die neue Datenstruktur die ID's mit 1 beginnt und damit der Array Index der ObjektID gleich sein soll (was ja grundsätzlich eine ausgezeichnete Idee ist).
Bob commited diese Änderung ins git, merged den spaß dann mit der Arbeit der letzten zwei Wochen und plötzlich funktioniert gar nichts mehr wie erwartet. Jetzt heißt es 2 Wochen an code changes von verschiedenen Entwicklern durchgehen um zu finden wo dieser Fehler aufgetreten ist. Also mit mindestens 5 stunden Arbeitszeit sollte man dafür schon rechnen.
0118999881999119725-3
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
-
Warf
- Beiträge: 2216
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Schleifen --> verwirrt
Ich hatte mich damals um das design der Datenstruktur sowie das laden und speichern zu kümmern. Der Pascal Anfänger war dann mit den Auswertungsalgorithmen für diese Datenstruktur zu tun. Wir hatten also eine strikte kapselung zwischen IO und Verarbeitung, sowie uns an alle OOP Standards gehalten. Um genau zu sein hat diese kapselung zwischen IO und Verarbeitung dafür gesorgt das ich den Bug zunächst im IO code gesucht hab, wodurch es sogar länger gedauert hat.m.fuchs hat geschrieben:Klingt nach schlechter Architektur, fehlender Kapselung und ungenügenden Unittests.
Und zu Unittests, was denkst du wie wir den Bug überhaupt gefunden haben? Ohne gute test Suites hat man praktisch keine Chance so einen Fehler überhaupt zu entdecken. (Denn Arr[0] schluckt der Computer einfach selbst wenn der Array erst bei 1 anfängt).
Um dir mal zu zeigen wie einfach es ist einen Fehler zu machen:
Sagen wir wir haben eine Unit SomeTestUnit mit einer Globalen Variable SomeVal:
Code: Alles auswählen
Unit SomeTestUnit;
interface
var SomeVal: Integer;
implementation
end.Code: Alles auswählen
program test;
uses
SomeTestUnit;
var
arr: array[20..50] of Integer;
i: Integer;
begin
for i:=0 to Length(arr)-1 do arr[i] := -1;
WriteLn(SomeVal);
end.Um das ganze mal auf die Spitze zu treiben, sagen wir mal du hast eine Globale variable einen Zeiger auf einem Config record, der alle Einstellungen wie Formgröße, Position, etc von deinem Programm speichert.
Jetzt hast du bei der Programmnutzung irgendwo eine stelle die genau den Fehler macht den ich oben gepostet habe, welcher den Pointer auf die Config überschreibt. Beim versuch die Config zu schreiben versucht das Programm dann den neuen ungültigen Pointer zu dereferenzieren, und sagen wir mal wir sind im Worst case, und keine SegFault fliegt dir um die Ohren. Stattdessen wird nur Müll gelesen und in die Config geschrieben. Beim nächsten lesen der config ist plötzlich alles falsch. Dein Fenster ist nicht mehr sichtbar, und beim Debuggen findest du heraus das einfach die Left und Top Koordinaten außerhalb des Bildschirms liegen.
Wo suchst du den Fehler? Im Config Load? im Config Write? in einer von hunderten For schleifen irgendwo zwischen 5000 Zeilen code, welche zufällig durch die Test suite nicht abgedeckt wurde, denn niemand glaubt man könnte eine simple füll mit Wert schleife über einem array verkacken (und test Suites sind nie allumfangend und testen oftmals solche Kleinigkeiten nicht)?
Das ist eine ganz gefährliche Fehlerquelle, die eigentlich bereits schon vom Sprachdesign selbst ausgeschlossen werden könnte. mMn. Ist das von der Fehleranfälligkeit auf dem selben Niveau wie C string Arithmetik
Zuletzt geändert von Warf am Di 10. Apr 2018, 20:31, insgesamt 1-mal geändert.
-
Mathias
- Beiträge: 7069
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Schleifen --> verwirrt
Wieso hast du nicht an der Stelle mit dem "false" ein {$R-} gesetzt und nacher wieder ein {$R+} ?So ähnlich ist es mir mal passiert, 1,5 Tage sind mir damals flöten gegangen nur um den Fehler zu finden. Man bekommt ja auch keine Fehlermeldung wenn man mal eben neben ein statisches array schreibt (Und range Checks musste ich deaktivieren da mir die dauernd false positives geworfen haben).
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
Warf
- Beiträge: 2216
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Schleifen --> verwirrt
Weil mir range Checks an so vielen stellen um die Ohren fliegen das ich sie immer global aus habe. Früher habe ich z.B. sehr oft solche Konstrukte gehabt:Mathias hat geschrieben:Wieso hast du nicht an der Stelle mit dem "false" ein {$R-} gesetzt und nacher wieder ein {$R+} ?So ähnlich ist es mir mal passiert, 1,5 Tage sind mir damals flöten gegangen nur um den Fehler zu finden. Man bekommt ja auch keine Fehlermeldung wenn man mal eben neben ein statisches array schreibt (Und range Checks musste ich deaktivieren da mir die dauernd false positives geworfen haben).
Code: Alles auswählen
Move(Arr1[0], Arr2[0], Len);Mittlerweile habe ich solche Konstrukte deutlich seltener, da ich für Strangs einfach .substring vom Typhelper verwenden kann, früher kam diese Zeile aber in den meisten meiner Programme mindestens einmal vor
-
mischi
- Beiträge: 206
- Registriert: Di 10. Nov 2009, 18:49
- OS, Lazarus, FPC: macOS, 10.13, lazarus 1.8.x, fpc 3.0.x
- CPU-Target: 32Bit/64bit
Re: Schleifen --> verwirrt
Des einen Nische, des anderen täglich (Physik-)Brot und genau deshalb ist mir die Flexibilität wichtig. Probleme mit false positives bei range checks hatte ich noch nie. Das habe ich bei Testläufen immer an und mach das nur bei zeitaufwendigen Produktionsläufen aus. Das mit der -1 für den Index hört sich einfach an, aber bei komplexeren Algorithmen kann das schnell unübersichtlich werden. Meistens ist die Verführung, den Ausdruck zu reduzieren, zu groß und bei einem von 20 Fällen wird ein Vorzeichen verschlampt.Warf hat geschrieben:PS: Fortran ist außerdem eine komplette nieschensprache, die kaum noch wer außerhalb von Physiklaboratorien verwendet. Also so irrelevant das jemand der heute programmieren lernt wahrscheinlich nie eine Zeile Fortran sehen wird (außer er ist Physik student), und das eine mal das das in der Lebzeit eines Programmierers mal passiert kann man auch ein -1 hinschreiben
MiSchi macht die fink-Pakete
-
mischi
- Beiträge: 206
- Registriert: Di 10. Nov 2009, 18:49
- OS, Lazarus, FPC: macOS, 10.13, lazarus 1.8.x, fpc 3.0.x
- CPU-Target: 32Bit/64bit
Re: Schleifen --> verwirrt
Hätte man da nicht besser gleich Pointer verwendet? Ich brauch das selten, weil meine arrays normalerweise die gleiche Länge haben und man einfach Arr2 := Arr1 machen kann.Warf hat geschrieben:Weil mir range Checks an so vielen stellen um die Ohren fliegen das ich sie immer global aus habe. Früher habe ich z.B. sehr oft solche Konstrukte gehabt:Wo mir der Rangeindicator immer um die Ohren geflogen ist (leerer array). Und da mein Programm einen string parser enthielt, kamen solche Konstrukte so oft vor, das ich range Checks einfach global ausgeschaltet habe.Code: Alles auswählen
Move(Arr1[0], Arr2[0], Len);
Zuletzt geändert von mischi am Di 10. Apr 2018, 20:55, insgesamt 1-mal geändert.
MiSchi macht die fink-Pakete
-
Mathias
- Beiträge: 7069
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Schleifen --> verwirrt
Mit diesem Problem bin ich auch schon konfrontiert worden. Als bei OpenGL ein Vertex-Array aus 0 Zeichen bestand.Wo mir der Rangeindicator immer um die Ohren geflogen ist (leerer array).
Mit
Code: Alles auswählen
Pointer(GLfloatArray) anstelle von @GLfloatArray[0]Bei Move würde ich ein if Len > 0 then einfügen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
Mathias
- Beiträge: 7069
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Schleifen --> verwirrt
Dies habe ich gerade versuch, geht aber nicht, da nicht die eigentlich Daten verschoben werden, sondern die Speicherbereiche, in dem sich Die Zeiger auf die Array befinden.Hätte man da nicht besser gleich Pointer verwendet?
Mit einer statischen Array könnte es gehen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
mischi
- Beiträge: 206
- Registriert: Di 10. Nov 2009, 18:49
- OS, Lazarus, FPC: macOS, 10.13, lazarus 1.8.x, fpc 3.0.x
- CPU-Target: 32Bit/64bit
Re: Schleifen --> verwirrt
Bei mir (fpc 3.0.4, OSX 10.13.4 High Sierra) kommt ohne range check 0 raus. Etwas besser, aber auch nicht gut. Hab vielleicht andere Voreinstellungen. Mit range check (-Cr) kommt ein run time error. In schönem Pascal code würde man aber auch einen subrange typ 20..50 deklarieren und beim array und bei i verwenden. Dann gibt es auch schon prompt einen Fehler beim Kompilieren. Zeigt das Beispiel nicht eher, dass man in Pascal auch C-ähnlich programmieren kann, sich dann aber auch mit den gleichen Problemen rumschlagen muss.Warf hat geschrieben: Um dir mal zu zeigen wie einfach es ist einen Fehler zu machen:
Sagen wir wir haben eine Unit SomeTestUnit mit einer Globalen Variable SomeVal:Dann gibt dieser Code auf meinem Mac (fpc 3.0.4, OSX 10.13.1 High Sierra) -1 zurück:Code: Alles auswählen
Unit SomeTestUnit; interface var SomeVal: Integer; implementation end.Code: Alles auswählen
program test; uses SomeTestUnit; var arr: array[20..50] of Integer; i: Integer; begin for i:=0 to Length(arr)-1 do arr[i] := -1; WriteLn(SomeVal); end.
MiSchi macht die fink-Pakete
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2855
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Re: Schleifen --> verwirrt
Ich denke wir können die Diskussion an dieser Stelle beenden. Also falls du meinst, dass du mit dem aufgezählten Dingen und deinem geposteten Quellcode noch von Architektur und Kapselung sprichst.Warf hat geschrieben:Um das ganze mal auf die Spitze zu treiben, sagen wir mal du hast eine Globale variable einen Zeiger auf einem Config record, der alle Einstellungen wie Formgröße, Position, etc von deinem Programm speichert.
0118999881999119725-3
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
-
Warf
- Beiträge: 2216
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Schleifen --> verwirrt
Oh wenn dir mein Beispiel nicht gut genug ist machen wir doch aus der globalen variable eines Record Zeigers einfach die Variable xxxForm: TxxxForm die von lazarus automatisiert erstellt wird und von der LCL benötigt wird. Und irgendwann rufst du xxxForm.ShowModal auf und es kracht. Ist im grunde das selbe Szenario mit der selben Problematik. Oder sagen wir mal du überschreibst die locale und bekommst plötzlich den Fehler: 123.456 ist kein gültiger Float wert, obwohl du extra amerikanische locale verwendest.m.fuchs hat geschrieben:Ich denke wir können die Diskussion an dieser Stelle beenden. Also falls du meinst, dass du mit dem aufgezählten Dingen und deinem geposteten Quellcode noch von Architektur und Kapselung sprichst.
Oder sind dir diese szenarien auch zu abstrakt? verwendest du etwa gar keine Lazarus forms oder FoatToStr?
Wenn du noch ein einfacheres Beispiel möchtest:
Code: Alles auswählen
procedure Foo();
var
i: Integer;
arr: array[15..30] of Integer;
Bar: TBar;
begin
//Bar wird initialisiert
// 40 Zeilen sonstiger Code
for i:=0 to Length(arr)-1 do arr[i] := -1;
// 40 Zeilen sonstiger code
Bar.FooBar;
end;Oder was ist wenn du die rücksprungaddresse des frames der vorigen Funktion mit +=3 überschreibst und plötzlich in einer komplett anderen Funktion 3 instructions übersprungen werden? Wie du den Fehler suchst will ich sehen.
Wenn du lust hast nimm einfach mal ein kleines test Projekt, und pass Start und Endindex deines Arrays beliebig an. Du kannst beliebig viel kaputt machen. Wenn dein Array z.B. ein Feld eines Objektes ist, kannst du einfach mal andere Felder überschreiben. Wenn es eine Lokale variable ist kannst du alle lokalen variablen des aktuellen Stack frames sowie aller vorigen stack frames überschreiben, sowie die Rücksprung oder die self Adresse. Als globale Variable kannst du alle globalen Variablen überschreiben. Das Fehlerpotential ist unerschöpflich. Je nach Architektur kannst du sogar den eigenen quellcode ändern (was bei der virtuellen Harvard Architektur moderner Betriebsysteme nicht so einfach geht). Und von den möglichen Sicherheitslücken falls man das array mit userinput füllt will ich gar nicht erst anfangen
Ich kenne kein anderes Pascal Feature was so eine hohe Fehleranfälligkeit hat, die sich selbst mit C messen kann
Zuletzt geändert von Warf am Di 10. Apr 2018, 22:11, insgesamt 5-mal geändert.