Hallo,
wenn ich folgende Funktion des öfteren aufrufe, kommt es zu sporadischen
Fehlern im Programmablauf. Der Speicher verändert sich an gewissen
Stellen.
Es liegt an der Übergabe des NULL-Zeigers, aber warum? Es ist eine 32Bit
Architektur von Atmel (sam d21)
Also du iterierst über a und b, und nimmst die als Index für "Setzen",
welches immer nur die Länge 2 hat. Dann liest du definitiv durchaus mal
zufällig kram vom Stack/Speicher.
Das selbe passiert mit "id" und "Param", werden gelesen, obwohl sie ggf.
NULL sind.
Der Code hat mir zu wenig defensive Checks eingebaut.
Evtl. würde es auch helfen, wenn du die Routine etwas aufspaltest in
verschiedene Funktionen - für die Verständlichkeit, wobei die an sich
gut ist mit den Variablennamen und Kommentaren. Auch wenn mir noch
zuviele Unbekannte da sind, bspw. wie groß InfoTable wirklich ist,
und warum es abhängig ist von SYS_AnzahlZustaende, müsste sie dann nicht
StateTable oder ZustandsTable heißen?
der code ist nicht gerade gut zu lesen, viel zu verschachtelt.
im ersten Moment kann ich aber keinen Fehler finden, kann es sein das es
in PS_ProgrammeUebernehmen() zu einen Fehler kommt.
Rufus Τ. F. schrieb:> Ralf schrieb:>> Es liegt an der Übergabe des NULL-Zeigers, aber warum?>> Kann nicht sein. Die Funktion wertet ihren zweiten Parameter nirgends> aus.
Leider doch. An zwei Stellen. Aber ich musste auch erst suchen.
Du setzt nirgends PROG_UNDEFINIERT. Wozu ist dann
der Check da? Du kannst nicht annehmen, dass deine
Strukturen und Variablen auf dem Stack mit 0 initialisiert
sind! Du setzt ja nur Prog[0] auf 0 mit:
Robin R. schrieb:> Das selbe passiert mit "id" und "Param", werden gelesen, obwohl sie ggf.> NULL sind.
Nö werden sie von dem Funktionsaufruf nicht. Wie willst du einen Pointer
denn auf Länge prüfen?
Rufus Τ. F. schrieb:> Kann nicht sein. Die Funktion wertet ihren zweiten Parameter nirgends> aus.
Eben aber wenn ich den Funktionsaufruf noppe funktioniert alles Stabil.
Nur dieser Aufruf verursacht den Fehler nach einigen Aufrufen.
Peter II schrieb:> im ersten Moment kann ich aber keinen Fehler finden, kann es sein das es> in PS_ProgrammeUebernehmen() zu einen Fehler kommt.
Passiert nichts dolles:
1
voidPS_ProgrammeUebernehmen(void)
2
{
3
// Übernimmt das aktivierte Programm mit der höchsten Priorität:
4
boolKeinProgrammGefunden=1;
5
6
// Prüfe auf Umschalten - Grundprogramm wird ignoriert:
7
for(uint8_ta=1;a<SYS_AnzahlZustaende;a++)
8
{
9
if(InfoTable[a].Aktiviert)
10
{
11
if(LS_AktuellesProgramm!=a)
12
{
13
LS_AktuellesProgramm=a;
14
LS_ProgrammNeuLaden=1;
15
}
16
17
KeinProgrammGefunden=0;
18
break;
19
}
20
}
21
22
// Wenn es keine aktivierten Programme gibt, schalte in das 0. Programm!
@ Ralf
Andere hier reagieren vielleicht anders und mehr, wie Du es wünschst,
aber ich muss leider sagen, dass ich mir diesen Code nicht antue.
Dafür gibt es abstrakte Gründe, die Dir vielleicht nützlich erscheinen,
vielleicht aber auch nicht:
Zum einen hast Du sehr viel Code in eine Funktion geklemmt. Das ist
nicht eigentlich falsch, führt aber dazu, dass sie nur mit hohem Aufwand
zu testen ist. Ich rate Dir, die Funktion in mehrere kleinere
aufzuteilen. Eine bewährte Daumenregel lautet: "Eine Aufgabe, eine
Funktion" und eine weitere, ebenso bewährte: "Eine Funktion, eine
Bildschirmseite" (also etwa 25 Zeilen à 80 Zeichen).
Zum anderen ist die Fehlerbeschreibung bzw. die Analyse zu
oberflächlich.
Es fängt damit an, dass eine NULL als Parameter an sich niemals einen
Fehler hervorruft, wenn man voraussetzt, dass als Parameter an sich ein
Zeiger zulässig ist. Das wusstest Du vielleicht nicht.
Aber wenn man das voraussetzt, dann ruft eine NULL effektiv einen Fehler
bei ihrer Verwendung in einem Ausdruck einen Fehler hervor. Die Stelle
an der das geschieht musst Du finden und mit einer Wahrscheinlichkeit
ist auch ziemlich offensichtlich, was da genau schiefläuft - leider
nicht immer, oft eben auch nicht.
Also bitte überarbeite Deinen Code. Überlege Dir Tests für die
Einzelfunktion und baue die dann in die Funktion PS_ProgrammEvent ein.
Ralf schrieb:> Rufus Τ. F. schrieb:>> Kann nicht sein. Die Funktion wertet ihren zweiten Parameter nirgends>> aus.>> Eben ...
Das sehe ich anders:
In dieser Zeile:
Ralf schrieb:> Robin R. schrieb:>> Das selbe passiert mit "id" und "Param", werden gelesen, obwohl sie ggf.>> NULL sind.>> Nö werden sie von dem Funktionsaufruf nicht. Wie willst du einen Pointer> denn auf Länge prüfen?
Was und ob du auf id oder Param zugreifst hängt allein vom Inhalt
in InfoTable[a] ab. Außerdem verstehe ich nicht was du mit Länge meinst.
Ein NULL-Pointer zeigt auf 0x0, und Lesezugriffe davon sind schonmal
bedenklich.
Ehrlich, bau mehr Sicherheitsprüfungen ein und initialisiere deine
Variablen richtig. Dann schau ob die Fehler weg gehen.
Ralf schrieb:> Theor schrieb:>> wird Param verwendet.>> Ja aber nicht bei dem konkreten Aufruf.
Können wir nicht ableiten aus dem unvollständigen
Code und Programmzustand.
Und wenn du genau weisst, was dein Code tut,
dann weisst du auch wo der Fehler ist.
Eine zweifach verschachtelte Schleife mit einer mehrdimensionalen
Statemachine in der inneren Schleife? WTF? Soll das später noch
irgendwer anderes warten, oder ist das bloß Wegwerfcode zum privaten
Eigengebrauch?
Nop schrieb:> Eine zweifach verschachtelte Schleife mit einer mehrdimensionalen> Statemachine in der inneren Schleife? WTF? Soll das später noch> irgendwer anderes warten, oder ist das bloß Wegwerfcode zum privaten> Eigengebrauch?
Nö, für die Tonne.
Ralf schrieb:> Theor schrieb:>> wird Param verwendet.>> Ja aber nicht bei dem konkreten Aufruf.
Richtig. Wenn Du den oben zitierten Aufruf machst, dann nicht.
Dann wird aber auch nichts weiter in der ganzen Funktion getan, als
Ralf schrieb:> void PS_ProgrammEvent(Event_Typ Event, uint8_t *Param, uint16_t len,
...
> if(len > sizeof(InfoTable[0].Param))
Du hast also einen Bytepointer *Param und eine Struct mit einem Member
.Param.
Ist zwar erlaubt, sollte man aber nicht tun, da es sehr verwirrend ist.
Nimm für verschiedene Sachen auch verschiedene Namen.
Wenn Du explizit für Parameter NULL zuläßt, solltest Du sie auch
explizit auf NULL testen.
Man kann überlange Zeilen auch sinnvoll umbrechen, C ist nicht
zeilensensitiv.
Das ganze sieht wahnsinnig verworren aus, erweckt aber den Eindruck, daß
man es viel einfacher und klarer schreiben könnte.
Peter D. schrieb:> Du hast also einen Bytepointer *Param und eine Struct mit einem Member> .Param.> Ist zwar erlaubt, sollte man aber nicht tun, da es sehr verwirrend ist.> Nimm für verschiedene Sachen auch verschiedene Namen.
Guter Anreiz, werde ich ändern!
Peter D. schrieb:> Das ganze sieht wahnsinnig verworren aus, erweckt aber den Eindruck, daß> man es viel einfacher und klarer schreiben könnte.
Ist es auch, ist eben eine Abarbeitung von Zuständen. Wenn ich in der
switch alles in Funktionen Packe, trägt das der Übersichtlichkeit bei,
das stimmt schon, mache ich ggf. auch.
Grade probiert:
>> [c]void PS_ProgrammEvent2(Event_Typ Event, uint8_t *Param, uint16_t len,> uint8_t *id);[c]>> Verursacht ähnliche Probleme.
Schön. Aber was sollen wir nun damit anfangen?
Du musst das Problem erstmal genau feststellen, beschreiben worin es
besteht. Ich wiederhole: Eine Null als Parameter verursacht an sich
keinen Fehler.
A. K. schrieb:> Sollte das> memset(&Prog[0], 0, sizeof(Prog[0]));> nicht eher> memset(Prog, 0, sizeof(Prog));> sein?
Erster produktiver Beitrag, habe ich grade auch bemerkt.
Genau dadurch entstand der Fehler !
Danke
Ralf schrieb:> Erster produktiver Beitrag, habe ich grade auch bemerkt.> Genau dadurch entstand der Fehler !
Danke, genauauf die Stelle hab ich schon vor dutzenden Posts
hingewiesen.
Liest denn keiner meine Posts oder muss man jetzt alles vorkauen?
Robin.R. schrieb:> Ralf schrieb:>> Erster produktiver Beitrag, habe ich grade auch bemerkt.>> Genau dadurch entstand der Fehler !>> Danke, genauauf die Stelle hab ich schon vor dutzenden Posts> hingewiesen.> Liest denn keiner meine Posts oder muss man jetzt alles vorkauen?
Tröste Dich. Da bist Du nicht der einzige hier.
Robin.R. schrieb:> Ralf schrieb:> Erster produktiver Beitrag, habe ich grade auch bemerkt.> Genau dadurch entstand der Fehler !>> Danke, genauauf die Stelle hab ich schon vor dutzenden Posts> hingewiesen.> Liest denn keiner meine Posts oder muss man jetzt alles vorkauen?
Stimmt, hatte ich überlesen, tut mir leid.
Können auch gerne mal über die Struktur diskutieren. Die Funktion
verknüpft Events mit Zuständen, klar kann ich in der switch die Code
Teile auslagern. Nur die erste switch Anweisung legt quasi die Zustände
fest, die durch ein Event geändert werden müssen und Prog ist die
"Warteschlange"