Forum: Mikrocontroller und Digitale Elektronik NULL Pointer als Übergabe in Funktion verursacht sporadische Fehler


von Ralf (Gast)


Lesenswert?

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)
1
PS_ProgrammEvent(EVENT_PUSHNACHRICHT, NULL, 0, NULL);

Header:
1
void PS_ProgrammEvent(Event_Typ Event, uint8_t *Param, uint16_t len, uint8_t *id);

Funktion:
1
void PS_ProgrammEvent(Event_Typ Event, uint8_t *Param, uint16_t len, uint8_t *id)
2
{
3
  uint8_t LenByte;
4
  Programm_Typ Prog[2];  
5
  bool Setzen[2];          // Standardmäßig löschen der Aktivierung des Programms.
6
7
  memset(&Prog[0], 0, sizeof(Prog[0]));
8
  
9
  // Prüfen auf Verträglichkeit
10
  if(len > sizeof(InfoTable[0].Param))
11
    return;
12
13
  // Prüft dein Event gegen und schaltet die Programme um, falls nötig.
14
  switch(Event)
15
  {  
16
    case EVENT_EINGEHENDER_ANRUF:
17
18
      // Ein Anruf geht gerade ein.
19
      Prog[0] = PROG_EINGEHENDER_ANRUF;
20
      Setzen[0] = 1;    
21
                        
22
    break;
23
    case EVENT_EINGEHENDER_ANRUF_BEENDET:
24
25
      // Eingehender Anruf ist beendet.      
26
      Prog[0] = PROG_EINGEHENDER_ANRUF;              
27
      Setzen[0] = 0;  
28
29
    break;
30
    case EVENT_VERPASSTER_ANRUF:
31
    
32
      Prog[0] = PROG_VERPASSTER_ANRUF;
33
      Setzen[0] = 1;
34
    
35
    break;
36
    case EVENT_VERPASSTER_ANRUF_GELESEN:
37
    
38
      Prog[0] = PROG_VERPASSTER_ANRUF;
39
      Setzen[0] = 0;
40
41
    break;
42
    case EVENT_VERPASSTE_PUSHNACHRICHT:
43
    
44
      Prog[0] = PROG_VERPASSTE_NACHRICHT;
45
      Setzen[0] = 1;
46
    
47
    break;
48
    case EVENT_PUSHNACHRICHT_GELESEN:  
49
      
50
      Prog[0] = PROG_VERPASSTE_NACHRICHT;
51
      Setzen[0] = 0;    
52
53
    break;
54
    case EVENT_PUSHNACHRICHT:  
55
        
56
      Prog[0] = PROG_EINGEHENDE_NACHRICHT;
57
58
    break;
59
    case EVENT_BLE_VERBUNDEN:
60
61
      Prog[0] = PROG_BLE_CONNECTED;
62
      Setzen[0] = 1;
63
64
      Prog[1] = PROG_BLE_DISCONNECTED;
65
      Setzen[1] = 0;
66
67
    break;
68
    case EVENT_BLE_GETRENNT:
69
70
      Prog[0] = PROG_BLE_CONNECTED;
71
      Setzen[0] = 0;
72
73
      Prog[1] = PROG_BLE_DISCONNECTED;
74
      Setzen[1] = 1;
75
76
    break;
77
  }    
78
79
  // Gehe alle Vorhaben durch:
80
  for(uint8_t b = 0; b < sizeof(Prog)/sizeof(Prog[0]); b++)
81
  {  
82
    if(Prog[b] == PROG_UNDEFINIERT)
83
      continue;
84
    
85
    // Gehe alle Programme durch.
86
    for(uint8_t a = 0; a < SYS_AnzahlZustaende; a++)
87
    {
88
      // Gibt es ein Programm, dass aktiviert werden kann?
89
      if(InfoTable[a].ProgTyp == Prog[b])
90
      {
91
        switch(InfoTable[a].ProgTyp)
92
        {
93
          // Programmtyp passt.
94
          case PROG_EINGEHENDER_ANRUF:
95
            // TODO: Prüfe die Parameter des Programms.
96
                    
97
            InfoTable[a].Aktiviert = Setzen[b];  
98
                                        
99
          break;
100
          case PROG_VERPASSTE_NACHRICHT:  
101
                  
102
            // Prüfe den Parameter (App-ID)
103
            if(Setzen[b])
104
            {
105
              // Prüfe den Parameter:
106
              LenByte = InfoTable[a].Param[0];
107
108
              if(!LenByte || LenByte <= PS_ProgrammInfoParaLaenge && len == LenByte && !memcmp(&InfoTable[a].Param[1], Param, LenByte))
109
              {
110
                // Speicher die Paket ID ab:
111
                memcpy(&InfoTable[a].NotiID[0], id, sizeof(InfoTable[a].NotiID));
112
                InfoTable[a].Aktiviert = Setzen[b];                
113
              }                        
114
            }
115
            else if(!Setzen[b] && !memcmp(&InfoTable[a].NotiID[0], id, sizeof(InfoTable[a].NotiID)))
116
            {  
117
              InfoTable[a].Aktiviert = Setzen[b];              
118
            } 
119
120
          break;
121
          case PROG_VERPASSTER_ANRUF:
122
            // Verpasster Anruf:
123
            if(Setzen[b])
124
            {
125
              // Länge des Namens:
126
              LenByte = InfoTable[a].Param[0];
127
128
              // Wenn die Länge des hinterlegten Namens kleiner ist als die des Parameters.
129
              if(LenByte < len)              
130
                len = LenByte;              
131
132
              if(!LenByte || LenByte <= (PS_ProgrammInfoParaLaenge - 1) && len == LenByte && !memcmp(&InfoTable[a].Param[1], Param, LenByte))
133
              {
134
                // Speicher die Paket ID ab:
135
                memcpy(&InfoTable[a].NotiID[0], id, sizeof(InfoTable[a].NotiID));
136
                InfoTable[a].Aktiviert = Setzen[b];
137
              }
138
            }
139
            else if(!Setzen[b] && !memcmp(&InfoTable[a].NotiID[0], id, sizeof(InfoTable[a].NotiID)))
140
            {
141
              InfoTable[a].Aktiviert = Setzen[b];
142
            }
143
          break;
144
          case PROG_EINGEHENDE_NACHRICHT:
145
146
            // Übergangsprogramm einer eingehenden Nachricht:
147
            InfoTable[a].Aktiviert = 1;
148
149
          break;
150
          case PROG_BLE_CONNECTED:
151
152
            InfoTable[a].Aktiviert = Setzen[b];
153
154
          break;
155
          case PROG_BLE_DISCONNECTED:
156
157
            InfoTable[a].Aktiviert = Setzen[b];
158
159
          break;
160
        }
161
      }
162
    }
163
  }
164
  
165
  PS_ProgrammeUebernehmen();
166
}

von R. R. (elec-lisper)


Lesenswert?

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?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ralf schrieb:
> Es liegt an der Übergabe des NULL-Zeigers, aber warum?

Kann nicht sein. Die Funktion wertet ihren zweiten Parameter nirgends 
aus.

von Peter II (Gast)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

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.

von R. R. (elec-lisper)


Lesenswert?

Was mir auch noch auffällt:
1
if(Prog[b] == PROG_UNDEFINIERT)

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:
1
memset(&Prog[0], 0, sizeof(Prog[0]));

von Ralf (Gast)


Lesenswert?

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
void PS_ProgrammeUebernehmen(void)
2
{
3
  // Übernimmt das aktivierte Programm mit der höchsten Priorität:
4
  bool KeinProgrammGefunden = 1;
5
6
  // Prüfe auf Umschalten - Grundprogramm wird ignoriert:
7
  for(uint8_t a = 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!
23
  if(KeinProgrammGefunden && LS_AktuellesProgramm != 0)
24
  {
25
    LS_AktuellesProgramm = 0;
26
    LS_ProgrammNeuLaden = 1;
27
  }
28
}

von Ralf (Gast)


Lesenswert?

Robin R. schrieb:
> Du setzt nirgends PROG_UNDEFINIERT.

Doch:
1
typedef enum{
2
  EVENT_EINGEHENDER_ANRUF,
3
  EVENT_EINGEHENDER_ANRUF_BEENDET,
4
5
  EVENT_VERPASSTER_ANRUF,
6
  EVENT_VERPASSTER_ANRUF_GELESEN,
7
8
  EVENT_VERPASSTE_PUSHNACHRICHT,
9
  EVENT_PUSHNACHRICHT_GELESEN,
10
11
  EVENT_BLE_VERBUNDEN,
12
  EVENT_BLE_GETRENNT,
13
14
  // Blending Programme (Übergangsprogramme):
15
  EVENT_PUSHNACHRICHT,
16
} Event_Typ;

von Theor (Gast)


Lesenswert?

@ 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.

von R. R. (elec-lisper)


Lesenswert?

Ralf schrieb:
> Robin R. schrieb:
>> Du setzt nirgends PROG_UNDEFINIERT.
>
> Doch:
>
>
1
> typedef enum{
2
3
Ähm? Ja? Und wo weist du PROG_UNDEFINIERT jemals zu?
4
5
> } Event_Typ;
6
>

von Theor (Gast)


Lesenswert?

Ralf schrieb:
> Rufus Τ. F. schrieb:
>> Kann nicht sein. Die Funktion wertet ihren zweiten Parameter nirgends
>> aus.
>
> Eben ...

Das sehe ich anders:

In dieser Zeile:
1
if(!LenByte || LenByte <= PS_ProgrammInfoParaLaenge && len == LenByte && !memcmp(&InfoTable[a].Param[1], Param, LenByte))

und in dieser Zeile
1
if(!LenByte || LenByte <= (PS_ProgrammInfoParaLaenge - 1) && len == LenByte && !memcmp(&InfoTable[a].Param[1], Param, LenByte))

wird Param verwendet.

von Ralf (Gast)


Lesenswert?

Ralf schrieb:
> Du setzt nirgends PROG_UNDEFINIERT.

Sry. Meinte das hier:
1
typedef enum{
2
  // Stabile Programme (Hauptprogramme)
3
  PROG_UNDEFINIERT = 0,                            // Undefiniertes Programm
4
  PROG_VERPASSTER_ANRUF = 1,                          // Nicht angenommener Anruf
5
  PROG_VERPASSTE_NACHRICHT = 2,                        // Ungelesene Nachricht
6
  PROG_EINGEHENDER_ANRUF = 3,                          // Eingehender Anruf
7
  PROG_BLE_CONNECTED = 4,                            // iPhone per Bluetooth verbunden
8
  PROG_BLE_DISCONNECTED = 5,
9
10
  // Blending Programme (Übergangsprogramme)
11
  PROG_EINGEHENDE_NACHRICHT = 254,
12
} Programm_Typ;

von R. R. (elec-lisper)


Lesenswert?

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.

von Ralf (Gast)


Lesenswert?

Theor schrieb:
> wird Param verwendet.

Ja aber nicht bei dem konkreten Aufruf.

von R. R. (elec-lisper)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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?

von Ralf (Gast)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

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
1
InfoTable[a].Aktiviert = 1;

was in der später geposteten Funktion PS_ProgrammeUebernehmen zunächst 
zu nichts weiter als setzen von zur Compile-Zeit definierten Variablen 
bewirkt.

Darin liegt der Fehler jedenfalls nicht.


Ich verweise nochmal auf meinen Beitrag hier: 
Beitrag "Re: NULL Pointer als Übergabe in Funktion verursacht sporadische Fehler"

von Peter D. (peda)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

Dachte das wäre zu unhöflich, aber ich möchte Peter zustimmen: Sehr 
wuselig das Ganze.

von Ralf (Gast)


Lesenswert?

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:
1
PS_ProgrammEvent2(EVENT_PUSHNACHRICHT, NULL, 0, NULL);
1
void PS_ProgrammEvent2(Event_Typ Event, uint8_t *Param, uint16_t len, uint8_t *id)
2
{
3
4
}

[c]void PS_ProgrammEvent2(Event_Typ Event, uint8_t *Param, uint16_t len, 
uint8_t *id);[c]

Verursacht ähnliche Probleme.

von Theor (Gast)


Lesenswert?

Ralf schrieb:
> Grade probiert:
>
>
1
PS_ProgrammEvent2(EVENT_PUSHNACHRICHT, NULL, 0, NULL);
>
>
1
void PS_ProgrammEvent2(Event_Typ Event, uint8_t *Param, uint16_t len, 
2
> uint8_t *id)
3
> {
4
> 
5
> }
>
> [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.

von Theor (Gast)


Lesenswert?

Ich glaub, ich gehe ins Bett und lese Don Quichotte. Das passt genau zum 
Thread. :-)

von Robin.R. (Gast)


Lesenswert?

Und ich nehm an meine vielfältigen Bemerkungen werden nicht verstanden?
Schreib ich überhaupt Deutsch? Oder schlaf ich schon?

von (prx) A. K. (prx)


Lesenswert?

Sollte das
  memset(&Prog[0], 0, sizeof(Prog[0]));
nicht eher
  memset(Prog, 0, sizeof(Prog));
sein?

von Ralf (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

A. K. schrieb:
> memset(&Prog[0], 0, sizeof(Prog[0]));

memset(&Prog[0], 0, sizeof(Prog));

von Peter II (Gast)


Lesenswert?

Ralf schrieb:
> memset(&Prog[0], 0, sizeof(Prog));

oder einfach gleich auf 0 setzen
1
Programm_Typ Prog[2] = {0};

von Ralf (Gast)


Lesenswert?

Peter II schrieb:
> oder einfach gleich auf 0 setzen

Setzt das nicht nur den ersten der Beiden auf null?

von Ralf (Gast)


Lesenswert?

Ralf schrieb:
> Setzt das nicht nur den ersten der Beiden auf null?

Ne, gute Idee!

von Peter II (Gast)


Lesenswert?

Ralf schrieb:
> Setzt das nicht nur den ersten der Beiden auf null?

nein, setzt alles auf 0

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:
> memset(&Prog[0], 0, sizeof(Prog));

ist identisch mit
  memset(Prog, 0, sizeof(Prog));

a[i] ist *(a+i)
&a[0] ist &*(a+0) ist a

von Robin.R. (Gast)


Lesenswert?

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?

von Theor (Gast)


Lesenswert?

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.

von Robin.R. (Gast)


Lesenswert?

@Theor: Danke ;-)

von Rafl (Gast)


Lesenswert?

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.

von Rafl (Gast)


Lesenswert?

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"

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.