Wert von Array an Stelle auslesen, wo Array nicht beschrieben ist

mikoborn

Level-1
Beiträge
56
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Morgen,

hier erstmal der Code:

IF fb_TON_Hydro.Q THEN
fb_TON_Hydro(IN:=FALSE);
nSchrittSuche := GVL.iSchritt + 1;

//Sucht nächsten Schritt, der ausgeführt werden kann
WHILE GVL.aHydroProgramm[1][nSchrittSuche] <= 0 AND GVL.iSchritt <= iSchrittgrenze DO
nSchrittSuche:=nSchrittSuche+1;
END_WHILE

//Schritt soll auf den nächsten Schritt gesetzt werden, der ausgeführt werden kann
GVL.iSchritt:=nSchrittSuche;

In Spalte 1 meines Arrays "GVL.aHydroProgramm" ist die Anzahl der Sekunden aufgeführt, die das Programm in GVL.iSchritt verweilen soll. Nun kommt es aber vor, dass ich zum Beispiel nur Schritt 1 und Schritt 4 ausführen möchte, weshalb ich die Variable nSchrittSuche eingeführt habe. Die Variabe prüft in einer Schleife ob der jeweils nächste Schritt eine Laufzeit eingetragen hat. Mein Array "GVL.aHydroProgramm" hat lediglich 10 Zeilen, also auch nur 10 Schritte. Nun ist mir aber aufgefallen, dass meine While-Schleife falsch ist. An der Variable GVL.iSchritt ändere ich innerhalb der Schleife ja nichts und damit wird nSchrittSuche ja irgendwann einen Wert annehmen, der über der Zeilenanzahl des Arrays liegt, wenn die andere Bedingung der While-Schleife auch erfüllt ist. Mit der Abfrage "GVL.aHydroProgramm[1][nSchrittSuche] <= 0" frage ich also einen Wert ab, den ich gar nicht geschrieben habe. Jetzt ist mir aber aufgefallen, dass GVL.iSchritt wiederholt auf 61 gesetzt wird.

Kann mir jemand erklären, was passiert, wenn ich einen Wert abfrage der außerhalb meines Arrays liegt und wieso GVL.iSchritt ausgerechnet auf 61 gesetzt wird?

Geschrieben ist das ganze in TWINCat 3
 
Wieso eine While- und keine For-Schleife?

Kann mir jemand erklären, was passiert, wenn ich einen Wert abfrage der außerhalb meines Arrays liegt
Du solltest dafür sorgen dass deine Arraygrenzen eingehalten werden, sonst schmeisst es dir natürlich einen Fehler raus auf der CPU, ich weiß gerade nicht wie sich die Twincat Runtime vehält, aber es könnte zum Runtime Stop führen.

wieso GVL.iSchritt ausgerechnet auf 61 gesetzt wird?
Da sich deine Variable bis auf den Wert anscheinend hoch-addiert
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Mehrere Dinge.
Erstmal solltest Du für Code bitte die Code Tags nehmen. Die kannst Du entweder von Hand eingeben oder auf den entsprechenden Button klicken. Den Button findest Du, indem Du über die drei Punkte weitere Buttons anzeigen lässt.
Bei der IF-Abfrage fehlt das END_IF.
Schrittgrenze wird zwar abgefragt, aber nie gesetzt.
Ich bin kein großer Freund von While und Repeat Schleifen, speziell bei der Nutzung von Arrays. Nimm hier lieber eine FOR-Schleife, die Du mit EXIT abbrechen kannst.
Wenn Du auf ein nicht existierendes Array Element lesend zugreifst erhältst Du die Daten, die an der entsprechenden Stelle im Speicher stehen. Bei einem schreibenden Zugriff kann es zum Absturz kommen oder Du bügelst die Daten anderer Variablen über.
 
Zuletzt bearbeitet:
Moin,

also zum Verständnis:
Code:
GVL.aHydroProgramm : ARRAY [1][1..iSchrittgrenze] of IrgendeinZeitFormat

Richtig?

Dann würde ich mal die While-Schleife (die ich aufgrund ihrer möglichen Unbegrenztheit sowieso ungerne einsetze) mal durch eine FOR-Schleife ersetzen:

Code:
FOR nSchrittsuche := 1 TO nSchrittsuche <= iSchrittgrenze DO  // Oder Du kannst nSchrittsuche auch anders initialisieren, z.B. mit GVL.iSchritt.
    IF GVL.aHydroProgramm[1][nSchrittSuche] >= 0 THEN
       EXIT;
    END_IF;
END_FOR;

Damit bleibst Du dann definitiv innerhalb Deiner Array-Grenzen, speziell wenn Du iSchrittgrenze als Konstante anlegst und damit auch die Array-Grenze definierst.

Wenn Du außerhalb des Arrays zugreifst, liest Du irgendetwas im Speicher, undefiniert, was gerade dort steht. Vermutlich steht da zufällig immer etwas, was Deine Bedingung erfüllst.
Solltest Du in Deiner SPS die Möglichkeit haben, Speicherverletzungen zu überwachen, würde die Steuerung vermutlich in Störung gehen, sobald Du den ersten Schritt außerhalb der Array-Grenzen setzt.

Deine WHILE-Bedingung fragt ja nicht auf eine Array-Grenze ab. Wenn Du alle nachfolgenden Zeiten auf -1 setzt, findet die While-Schleife ja nichts, wo sie enden kann, bis irgendwo im Speicher eine Zahl >= 0 steht.
 
Du solltest dafür sorgen dass deine Arraygrenzen eingehalten werden, sonst schmeisst es dir natürlich einen Fehler raus auf der CPU, ich weiß gerade nicht wie sich die Twincat Runtime vehält, aber es könnte zum Runtime Stop führen.
Da passiert bei einem lesenden Zugriff in TC3 bei Nutzung einer Variablen als Index gar nichts, außer dass man "falsche" Daten erhält, wie bei allen anderen Steuerungen übrigens auch.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Wieso eine While- und keine For-Schleife?


Du solltest dafür sorgen dass deine Arraygrenzen eingehalten werden, sonst schmeisst es dir natürlich einen Fehler raus auf der CPU, ich weiß gerade nicht wie sich die Twincat Runtime vehält, aber es könnte zum Runtime Stop führen.


Da sich deine Variable bis auf den Wert anscheinend hoch-addiert
Ja das ist logisch, aber wieso hört das hochaddieren ausgerechnet bei 61 auf? Anscheinend ist ja dann die Bedingung der While-Schleife nicht mehr erfüllt. "GVL.iSchritt <= iSchrittgrenze" kann es aber nicht sein, da ich an GVL.iSchritt ja nicht ändere. Also muss "GVL.aHydroProgramm[1][nSchrittSuche] <= 0" die BEdingung sein, die nicht mehr erfüllt wird. Das würde aber ja heißen, dass "GVL.aHydroProgramm[1][61]" nun größer 0 ist, für diese Stelle des Arrays habe ich allerdings nie einen Wert eingegeben
 
Dann würde ich mal die While-Schleife (die ich aufgrund ihrer möglichen Unbegrenztheit sowieso ungerne einsetze) mal durch eine FOR-Schleife ersetzen:

Code:
FOR nSchrittsuche := 1 TO nSchrittsuche <= iSchrittgrenze DO  // Oder Du kannst nSchrittsuche auch anders initialisieren, z.B. mit GVL.iSchritt.
    IF GVL.aHydroProgramm[1][nSchrittSuche] >= 0 THEN
       EXIT;
    END_IF;
END_FOR;

Damit bleibst Du dann definitiv innerhalb Deiner Array-Grenzen, speziell wenn Du iSchrittgrenze als Konstante anlegst und damit auch die Array-Grenze definierst.
Eine Konstante braucht es dafür gar nicht. Die Obergrenze kann man sich auch dynamisch errechnen.
Code:
FOR iCounter := 1 TO SIZEOF(ArrayVar) / SIZEOF(ArrayVar[0]) DO
    Anweisung;
END_FOR
 
Das würde aber ja heißen, dass "GVL.aHydroProgramm[1][61]" nun größer 0 ist, für diese Stelle des Arrays habe ich allerdings nie einen Wert eingegeben
Wir haben Dir oben erklärt, daß wenn Du außerhalb des Arrays zugreifst, Du irgendwo im Speicher hingreifst.
Das ist undefiniert.
Zufällig wird da dann immer an Stelle 61 irgendwas stehen, was das Programm als >=0 interpretiert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

also zum Verständnis:
Code:
GVL.aHydroProgramm : ARRAY [1][1..iSchrittgrenze] of IrgendeinZeitFormat

Richtig?

Dann würde ich mal die While-Schleife (die ich aufgrund ihrer möglichen Unbegrenztheit sowieso ungerne einsetze) mal durch eine FOR-Schleife ersetzen:

Code:
FOR nSchrittsuche := 1 TO nSchrittsuche <= iSchrittgrenze DO  // Oder Du kannst nSchrittsuche auch anders initialisieren, z.B. mit GVL.iSchritt.
    IF GVL.aHydroProgramm[1][nSchrittSuche] >= 0 THEN
       EXIT;
    END_IF;
END_FOR;

Damit bleibst Du dann definitiv innerhalb Deiner Array-Grenzen, speziell wenn Du iSchrittgrenze als Konstante anlegst und damit auch die Array-Grenze definierst.

Wenn Du außerhalb des Arrays zugreifst, liest Du irgendetwas im Speicher, undefiniert, was gerade dort steht. Vermutlich steht da zufällig immer etwas, was Deine Bedingung erfüllst.
Solltest Du in Deiner SPS die Möglichkeit haben, Speicherverletzungen zu überwachen, würde die Steuerung vermutlich in Störung gehen, sobald Du den ersten Schritt außerhalb der Array-Grenzen setzt.

Deine WHILE-Bedingung fragt ja nicht auf eine Array-Grenze ab. Wenn Du alle nachfolgenden Zeiten auf -1 setzt, findet die While-Schleife ja nichts, wo sie enden kann, bis irgendwo im Speicher eine Zahl >= 0 steht.
Die Idee klingt gut und die hatte ich auch bereits. Mein Kollege (der sehr viel mehr Ahnung hat als ich) meinte allerdings, dass man Schleifen nicht abbrechen soll. Ich habe das ehrlich gesagt nicht wirklich hinterfragt und einfach so hingenommen. Gibt es einen Grund dafür, weshalb das "schlechter" oder "unschöner" wäre? Ich werde dennoch mal eine For-Schleife daraus machen.
 
Eine Konstante braucht es dafür gar nicht. Die Obergrenze kann man sich auch dynamisch errechnen.
Code:
FOR iCounter := 1 TO SIZEOF(ArrayVar) / SIZEOF(ArrayVar[0]) DO
    Anweisung;
END_FOR
Das ist sicherlich korrekt... übersichtlicher wird's dadurch nicht ;-)
Wenn ich so variable programmiere, ist die Konstante bei mir in einem Konfigurationsabschnitt hinterlegt, dann kann ich damit sowohl das Array definieren als auch in allen Schleifen die Grenzen abfragen. Und der Code ist sprechender.
Aber jeder nach seinem Geschmack :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die Idee klingt gut und die hatte ich auch bereits. Mein Kollege (der sehr viel mehr Ahnung hat als ich) meinte allerdings, dass man Schleifen nicht abbrechen soll. Ich habe das ehrlich gesagt nicht wirklich hinterfragt und einfach so hingenommen. Gibt es einen Grund dafür, weshalb das "schlechter" oder "unschöner" wäre? Ich werde dennoch mal eine For-Schleife daraus machen.
Ich kenne eigentlich keinen Grund, warum man Schleifen nicht abbrechen sollte.
Andererseits sprechen viele Gründe dagegen, Schleifen zu nutzen, die potentiell endlos sein können.
 
Ja das ist logisch, aber wieso hört das hochaddieren ausgerechnet bei 61 auf? Anscheinend ist ja dann die Bedingung der While-Schleife nicht mehr erfüllt. "GVL.iSchritt <= iSchrittgrenze" kann es aber nicht sein, da ich an GVL.iSchritt ja nicht ändere. Also muss "GVL.aHydroProgramm[1][nSchrittSuche] <= 0" die BEdingung sein, die nicht mehr erfüllt wird. Das würde aber ja heißen, dass "GVL.aHydroProgramm[1][61]" nun größer 0 ist, für diese Stelle des Arrays habe ich allerdings nie einen Wert eingegeben
Das brauchst Du auch nicht. Wie schon erklärt greift TC3 auf eine Adresse im Speicher entsprechend des Index und des Variablentyps des Arrays zu.
Angenommen Dein Array geht von 1 bis 10 und startet an Adresse 1001 und ist vom Typ USINT, dann würde ein Zugriff auf das Element 10 bedeuten, dass auf die Adresse 1010 zugegriffen wird, was ja auch noch zulässig ist. Versuchst Du jetzt auf das, nicht existierende, Element 11 zuzugreifen landest Du bei Adresse 1011 und erhältst die Daten an die gerade dieser Stelle abgelegt sind.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wäre das nicht auch eine gute Lösung?:

WHILE GVL.aHydroProgramm[1][nSchrittSuche] <= 0 AND GVL.iSchritt <= iSchrittgrenze DO
nSchrittSuche:=nSchrittSuche+1;
END_WHILE

Somit spare ich mir ein IF und ein EXIT und bewege mich dennoch nur in den Arraygrenzen
 
Hier nun auch noch mein Kommentar dazu :
- warum beharrst du so auf deiner While-Schleife ? Es wurde dir doch nun mehrfach empfohlen es mit FOR .. TO zu machen - das wäre auch mein Ansatz ...
- dann hast du am Anfang des gesposteten Code-Teils eine bedingte Timer-Bearbeitung. Das kann natürlich im Einzelfall funktionieren (bei Einhaltung aller Spielregeln) - empfehlen tut es sich aber nicht ...
- last not least wäre es auch noch hilfreich den vollständigen Code zu sehen - also auch die Deklarationen ...
 
Hier nun auch noch mein Kommentar dazu :
- warum beharrst du so auf deiner While-Schleife ? Es wurde dir doch nun mehrfach empfohlen es mit FOR .. TO zu machen - das wäre auch mein Ansatz ...
- dann hast du am Anfang des gesposteten Code-Teils eine bedingte Timer-Bearbeitung. Das kann natürlich im Einzelfall funktionieren (bei Einhaltung aller Spielregeln) - empfehlen tut es sich aber nicht ...
- last not least wäre es auch noch hilfreich den vollständigen Code zu sehen - also auch die Deklarationen ...
Ich beharre nicht auf einer While-Schleife. Ich bin gerne für die beste Lösung offen, aber ich sehe gerade nicht, weshalb

FOR nSchrittsuche :=GVLiSchritt TO nSchrittsuche <= iSchrittgrenze DO
IF GVL.aHydroProgramm[1][nSchrittSuche] >= 0 THEN
EXIT;
END_IF;
END_FOR;

onjektiv besser ist als

WHILE GVL.aHydroProgramm[1][nSchrittSuche] <= 0 AND GVL.iSchritt <= iSchrittgrenze DO
nSchrittSuche:=nSchrittSuche+1;
END_WHILE

Wenn es konkrete Argumente gibt, nehme ich die For-Schleife gerne an
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wäre das nicht auch eine gute Lösung?:

WHILE GVL.aHydroProgramm[1][nSchrittSuche] <= 0 AND GVL.iSchritt <= iSchrittgrenze DO
nSchrittSuche:=nSchrittSuche+1;
END_WHILE

Somit spare ich mir ein IF und ein EXIT und bewege mich dennoch nur in den Arraygrenzen
Dann erkläre mir mal bitte, wie Du Dich damit innerhalb der Schrittgrenze hälst.
Wenigstens müßtest Du ein AND nSchrittSuche <= iSchrittgrenze ergänzen, weil Du sonst niemals innerhalb der selbst gesetzten Grenzen bleibst. Dein GVL.iSchritt ist immer kleiner, weil Du den nicht erhöhst. Und dann bist Du ausschließlich darauf angewiesen, daß mindestens Dein letztes Array-Element eine Zeit enthält.
 
Dann erkläre mir mal bitte, wie Du Dich damit innerhalb der Schrittgrenze hälst.
Wenigstens müßtest Du ein AND nSchrittSuche <= iSchrittgrenze ergänzen, weil Du sonst niemals innerhalb der selbst gesetzten Grenzen bleibst. Dein GVL.iSchritt ist immer kleiner, weil Du den nicht erhöhst. Und dann bist Du ausschließlich darauf angewiesen, daß mindestens Dein letztes Array-Element eine Zeit enthält.
Weil bei einer FOR-Schleife immer feststeht, wann Sie beendet ist, bei einer WHILE oder REPEAT Schleife aber nicht und das ist im SPS Umfeld ungünstig (Endlosschleife, Zykluszeit), natürlich kann auch eine FOR-Schleife Probleme machen, wenn die Ausführung zu lange dauert, aber das ist eher seltener.
 
Ich beharre nicht auf einer While-Schleife. Ich bin gerne für die beste Lösung offen, aber ich sehe gerade nicht, weshalb

FOR nSchrittsuche :=GVLiSchritt TO nSchrittsuche <= iSchrittgrenze DO
IF GVL.aHydroProgramm[1][nSchrittSuche] >= 0 THEN
EXIT;
END_IF;
END_FOR;

onjektiv besser ist als

WHILE GVL.aHydroProgramm[1][nSchrittSuche] <= 0 AND GVL.iSchritt <= iSchrittgrenze DO
nSchrittSuche:=nSchrittSuche+1;
END_WHILE

Wenn es konkrete Argumente gibt, nehme ich die For-Schleife gerne an
Dann erkläre mir mal bitte, wie Du Dich damit innerhalb der Schrittgrenze hälst.
Wenigstens müßtest Du ein AND nSchrittSuche <= iSchrittgrenze ergänzen, weil Du sonst niemals innerhalb der selbst gesetzten Grenzen bleibst. Dein GVL.iSchritt ist immer kleiner, weil Du den nicht erhöhst. Und dann bist Du ausschließlich darauf angewiesen, daß mindestens Dein letztes Array-Element eine Zeit enthält.
Sorry, hatte ich aus meinem alten Kommentar hier im Forum kopiert, das steht so nicht mehr in meinem Code.
 
Zurück
Oben