Forum: Mikrocontroller und Digitale Elektronik Eine unsinnige Warnung! Oder?


von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Endlich habe ich auch mal wieder ein Problemchen, wo mir keine Lösung zu 
einfällt.

Beim anpassen einer meiner Arduino Libraries (eine geschrumpfte 
ProtoThreads Implementierung) haut es mir folgende Warnung um die Ohren:
> warning: storing the address of local variable
> 'taskLable39' in 'lable' [-Wdangling-pointer=]
Die Meldung wird 3 mal geworfen, für alle 3 Lables im folgenden 
Beispiel, dazu noch eine kleine Horde an verschiedenen note: folge 
Meldungen.

µC:
STM32F411CE

Compiler:
xpack-arm-none-eabi-gcc\\12.2.1-1.2/bin/arm-none-eabi-g++

Verwunderlich ist daran, dass es keine Variable namens "taskLable39" im 
Code gibt.

Hier der Code:
1
#define INNER_CONCAT(s1,s2) s1##s2
2
#define CONCAT(s1,s2) INNER_CONCAT(s1,s2)
3
4
#define TaskBlock if(!lable)    \
5
  lable = &&taskLableStart;     \
6
  goto *lable;                  \
7
  taskLableStart:               \
8
  while(1)
9
10
#define PREFIX taskLable
11
12
#define taskSwitch()                      \
13
  do {                                    \
14
    lable = &&CONCAT(PREFIX,__LINE__);    \
15
    return;                               \
16
    CONCAT(PREFIX,__LINE__): ;            \
17
    } while(0)
18
19
#define taskPause(Task_interval)                                \
20
  timeStamp = millis();                                         \
21
  while((millis() - timeStamp) < (Task_interval)) taskSwitch()
22
23
#define taskWaitFor(Task_condition) while(!(Task_condition)) taskSwitch();
24
25
void setup()
26
{
27
  Serial.begin(9600);
28
  while(not Serial) {}
29
}
30
31
void task()
32
{
33
  static void *lable;
34
  static unsigned long timeStamp;
35
36
  TaskBlock
37
  {
38
    Serial.print("Start: ");  Serial.println(__FILE__);
39
    taskPause(1000);
40
41
    while(1)
42
    {
43
      Serial.println("anfang");
44
      taskPause(1000);
45
46
      Serial.println("ende");
47
      taskPause(1000);
48
    }
49
  }
50
}
51
52
void loop()
53
{
54
  task();
55
}

-----------

Und hier, was der Präprozessor daraus macht:
1
void setup()
2
{
3
  Serial1.begin(9600);
4
  while(not Serial1) {}
5
}
6
7
void task()
8
{
9
  static void *lable;
10
  static unsigned long timeStamp;
11
12
  if(!lable) lable = &&taskLableStart; goto *lable; taskLableStart: while(1)
13
  {
14
    Serial1.print("Start: "); Serial1.println("E:\\Programme\\arduino\\portable\\sketchbook\\sketch_feb08c001\\sketch_feb08c001.ino");
15
    timeStamp = millis(); while((millis() - timeStamp) < (1000)) do { lable = &&taskLable39; return; taskLable39: ; } while(0);
16
17
    while(1)
18
    {
19
      Serial1.println("anfang");
20
      timeStamp = millis(); while((millis() - timeStamp) < (1000)) do { lable = &&taskLable44; return; taskLable44: ; } while(0);
21
22
      Serial1.println("ende");
23
      timeStamp = millis(); while((millis() - timeStamp) < (1000)) do { lable = &&taskLable47; return; taskLable47: ; } while(0);
24
    }
25
  }
26
}
27
28
29
void loop()
30
{
31
  task();
32
}


-----------
Der Code funktioniert, nur die Meldungen nerven.
Gibt es eine Abhilfe, ohne die dangling-pointer Warnung ganz 
abzuschalten?
Warum ist taskLableStart keine lokale Variable (wirft keine Warnung) 
aber die anderen schon?
Ich sehe da keinen Unterschied.

: Bearbeitet durch User
von Hmmm (hmmm)


Lesenswert?

Arduino F. schrieb:
> Verwunderlich ist daran, dass es keine Variable namens "taskLable39" im
> Code gibt.

Jein:

Arduino F. schrieb:
> #define PREFIX taskLable

Arduino F. schrieb:
> lable = &&CONCAT(PREFIX,__LINE__);    \

Die Zahlen dahinter sind die jeweiligen Zeilenzahlen.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Hmmm schrieb:
> Die Zahlen dahinter sind die jeweiligen Zeilenzahlen.

Das ist mir klar, ist auch so beabsichtigt!

Aber für mich ist ein Label ein Label und keine lokale Variable.
Egal ob da eine Zahl dranhängt.
Die Zahl ist nicht verboten.

 lable = &&taskLableStart
Wirft keine Warnung.

: Bearbeitet durch User
von Hmmm (hmmm)


Lesenswert?

Arduino F. schrieb:
> Das ist mir klar, ist auch so beabsichtigt!

Achso, ich dachte, deine Verwirrung bezog sich hauptsächlich auf das 
Anhängsel.

Ich vermute, dass gewarnt wird, weil Deine Label-Pointer ausserhalb der 
Funktion unbrauchbar sind, also dangling.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Hmmm schrieb:
> Ich vermute, dass gewarnt wird, weil Deine Label-Pointer ausserhalb der
> Funktion unbrauchbar sind, also dangling.

Nöö...
Bleibt ja alles in der Funktion.

Arduino F. schrieb:
> lable = &&taskLableStart
> Wirft keine Warnung.

taskLableStart ist auch nicht von außen erreichbar.
Selbst wenn ich beide Vorkommen in taskLableStart4711 ändere, keine 
Meldung und funktioniert wie gewollt.
Die Zahlen sind es nicht, die mir Sorgen machen.

: Bearbeitet durch User
von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Zuerst mal, der Mechanismus der da benutzt wird ist eine GCC-Erweiterung

"Labels as Values"
https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

Damit sollte die Frage
> Aber für mich ist ein Label ein Label und keine lokale Variable.
beantwortet sein. Mit der GCC-Erweiterung ist ein Label ein Wert, der 
einer Variablen vom Typ "void *" zugewiesen werden kann.

Genau das wird im Code mehrfach gemacht. Ein Label wird mit dem 
&&-Operator geholt und der Variable "label" zugewiesen.

Aber, und da hat der Compiler recht, die Scopes der Label (also Werte) 
taskLabel39, taskLabel44 und taskLabel47 sind im (unterhalb des) Scope 
der Variable "label" verschachtelt. Und zwar 2 (39) bzw. 3 Ebenen (44, 
47) tiefer (wenn ich mich nicht verzählt habe).

Die "Labels as Values" werden nach oben in einem Scope gespeichert wo 
sie nach Meinung des Compilers (und ich glaube zurecht) nicht immer 
gültig sind.

Der Task-Switching Code springt über die Dangling Pointer in tiefer 
liegende Scopes ein, und niemand weiß wie der Stack zu dem Zeitpunkt für 
die Scopes aussieht. Dass das funktioniert ist Glück.

In der verlinkten Beschreibung steht auch drin (erster Satz):
>> You can get the address of a label defined in the current function
>> (or a containing function)
D.h. in andere Richtung geht es. In einem nested Scope den Wert eines 
Labes in einem umgebenden, höheren Scope darf man holen und benutzen - 
um damit aus einem umgebenden Scope wieder nach oben zu springen. Bei 
deinem Code wird aber nicht nach oben gesprungen, sondern nach unten 
oder seitwärts.

(unten, oben, seitwärts im Sinne der Verschachtelung der Scopes, nicht 
der Programmzeilen).

> taskLableStart ist auch nicht von außen erreichbar.

Nein, aber wird im gleichen Scope zugewiesen und benutzt, in dem "label" 
deklariert ist. Damit ist der Pointer in dem Scope gültig und wird nicht 
angemeckert.

Noch ein Zitat aus der Beschreibung

>> You may not use this mechanism to jump to code in a different function.
>> If you do that, totally unpredictable things happen. The best way to
>> avoid this is to store the label address only in automatic variables
>> and never pass it as an argument.

Mein Fazit, der Code ist Scheiße und reine Glückssache dass das 
funktioniert.

: Bearbeitet durch User
von Andreas M. (amesser)


Lesenswert?

Wie kommt man eigentlich auf solch einen Mist? Du arbeitet hier auf 
einem potenten CM4 und murkst mit so einem Protothreads ähnlichen Zeug 
rum? Wenn schon kooperatives Multitasking dann wenigstens über 
vernünftige Statemachines.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Andreas M. schrieb:
> Wie kommt man eigentlich auf solch einen Mist?

Hat er doch geschrieben
> Arduino F. schrieb:
>> Beim anpassen einer meiner Arduino Libraries

von Jens K. (jensky)


Lesenswert?

Windows Gerätemanager:
USB-Gerät, deaktivieren:
"Wenn Sie dieses Gerät deaktivieren, funktioniert es nicht mehr!"

:-)

von J. S. (jojos)


Lesenswert?

Die Protothreads sind doch keine Arduino Erfindung, die gibt es schon 
lange und andere benutzen die auch.
Ist das eine selbst modifizierte Variante? Ich sehe auch nicht das die 
lableTaskXX verschachtelt sind, die sind im Scope von lable. Aber warum 
ist lable static? Kann das das Problem sein? Der static Pointer wäre 
doch ungültig wenn er auf Stackvariablen zeigt.
Und wer hat das 'lable' genannt?

von Harald K. (kirnbichler)


Lesenswert?

J. S. schrieb:
> Und wer hat das 'lable' genannt?

Vielleicht jemand, der meint, daß es britischer klingt, wenn man die 
E-Konsonant-Kombination am Wortende durch Konsonant-E ersetzt, wie es in 
"Centre" und "Theatre" geschieht, (statt des amerikanischen "Center" und 
"Theater").

Die Zehennägel stellt es trotzdem auf.

von Steve van de Grens (roehrmond)


Lesenswert?

Jens K. schrieb:
> "Wenn Sie dieses Gerät deaktivieren, funktioniert es nicht mehr!"

Meine Lieblings-Meldung war
> "Keybbard error, press F1 to resume"

Ich erinnere mich an Server, an denen deswegen immer eine Tastatur 
hängen musste. Sie konnten sonst nicht booten.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

J. S. schrieb:
> Aber warum ist lable static?
Wäre es global, könnte es nur eine Task geben. Oder eben für jede Task 
eine globale mit eigenem Bezeichner
Die Funktion test() wird auf dem STM32 ein paar Millionen mal pro 
Sekunde aufgerufen.
Über all die Aufrufe muss das Sprungziel gemerkt werden.
Also darum:  static.


In meinem original Code ist es eine Instanzeigenschaft (nicht static), 
dort bin ich auf die Meldungen gestoßen.
Das oben gezeigte Beispiel ist eine Minimal Version um die Meldung zu 
reproduzieren.


---------------


Dieses Beispiel hier wirft die Meldung berechtigt:
1
int *merker;            
2
3
void setup() 
4
{
5
  int test = 42;
6
7
  Serial.begin(9600);
8
  merker = &test;
9
}
10
11
void loop() 
12
{
13
  Serial.println(*merker);
14
}

Es wirft:
> warning: storing the address of local variable 'test' in 'merker' 
[-Wdangling-pointer=]

Logisch, die lokale Variable verfällt nach dem verlassen der Funktion 
setup(), und der pointer ist "dangling".
Da ist die Meldung korrekt!

Das Beispiel im Eingangsposting hat keine verfallenden Variablen. 
Nirgendwo.


-------------

Hannes J. schrieb:
> "Labels as Values"
> https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
>
> Damit sollte die Frage
>> Aber für mich ist ein Label ein Label und keine lokale Variable.
> beantwortet sein. Mit der GCC-Erweiterung ist ein Label ein Wert, der
> einer Variablen vom Typ "void *" zugewiesen werden kann.

Deiner Logik kann ich nicht folgen. Der void Zeiger wird nicht 
angemeckert.
Sondern die angebliche Variable namens "taskLable39"
Der verlinkte Text ist mir natürlich bekannt.
Mit keiner einzigen Zeile verstoße ich gegen die dort genannten 
Bedingungen.
Oder habe ich da was übersehen?

------------

J. S. schrieb:
> Ist das eine selbst modifizierte Variante?
Ja!
Das Original von Adam Dunkels verwendet switch/case als Mechanismus. Was 
es schwieriger macht switch/case dann noch selber in der Task nutzen zu 
wollen.
Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste 
zu einer Kette von Vergleichen, welche es sehr langsam macht, ins 
Besondere für die weit hinten liegenden Fälle.
Die goto Variante ist immer gleich schnell.

----------


Andreas M. schrieb:
> Wie kommt man eigentlich auf solch einen Mist? Du arbeitet hier auf
> einem potenten CM4 und murkst mit so einem Protothreads ähnlichen Zeug
> rum? Wenn schon kooperatives Multitasking dann wenigstens über
> vernünftige Statemachines.
Es ist eine Statemachine.

Das Verfahren funktioniert.
Mein Ziel ist es, es auf allen Arduino üblichen Plattformen (ohne 
Meldung) zum laufen zu bekommen.
Bisher läuft es vom Tiny85(alle 8Bit AVR) über alle ESP bis gar zum K210 
mit seinen 64Bit.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Hannes J. schrieb:
> Noch ein Zitat aus der Beschreibung
>
>>> You may not use this mechanism to jump to code in a different function.
>>> If you do that, totally unpredictable things happen. The best way to
>>> avoid this is to store the label address only in automatic variables
>>> and never pass it as an argument.

Das trifft auf meinen Code nicht zu, bleibt alles in der gleichen 
Funktion.

Hannes J. schrieb:
> Mein Fazit, der Code ist Scheiße und reine Glückssache dass das
> funktioniert.
Wieso Glückssache?
Wogegen verstoße ich?

: Bearbeitet durch User
von Klaus (feelfree)


Lesenswert?

Vermutung: Der Compiler behandelt die labels wie lokale Variablen (was 
deren Lebensdauer betrifft). Das erklärt dann die Warnungen, und auch, 
warum es bei taskLableStart nicht auftritt.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Klaus schrieb:
> Der Compiler behandelt die labels wie lokale Variablen
Ja!
Die Frage ist, warum tut er das?
Denn es ist ja ein Label und keine Variable.
Oder viel wichtiger, wie kann ich im das abgewöhnen, das Label als 
Variable zu betrachten?

Hannes J. schrieb:
> Die "Labels as Values" werden nach oben in einem Scope gespeichert wo
> sie nach Meinung des Compilers (und ich glaube zurecht) nicht immer
> gültig sind.
>
> Der Task-Switching Code springt über die Dangling Pointer in tiefer
> liegende Scopes ein, und niemand weiß wie der Stack zu dem Zeitpunkt für
> die Scopes aussieht. Dass das funktioniert ist Glück.

Das ist natürlich richtig.
Zumindest das mit den Anweisungsblöcken, da springe "ich" in tiefere 
Ebenen.
Aber das mit dem Stack, das ist irrelevant.
Da ist nix mit Stacknutzung!

Damit ist:
> nicht immer gültig sind.
falsch

Und auch:
> und niemand weiß wie der Stack zu dem Zeitpunkt für
> die Scopes aussieht. Dass das funktioniert ist Glück.
Irrelevant, da keine Stacknutzung.

: Bearbeitet durch User
von Klaus (feelfree)


Lesenswert?

Arduino F. schrieb:
> Irrelevant, da keine Stacknutzung.

Das ist für den Compiler aber nur mit Zusatzaufwand zu erkennen.
Würde ich den Compiler bauen müssen, hätte ich mich auch so entschieden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Lable: ein kleines label?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Wilhelm M. schrieb:
> Lable: ein kleines label?

Meinen Dank, für diesen nützliche Ansatz.

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino F. schrieb:
> Wilhelm M. schrieb:
>> Lable: ein kleines label?
>
> Meinen Dank, für diesen nützliche Ansatz.

Sorry, aber Du lieferst immer wieder Steilvorlagen ;-)

von Klaus (feelfree)


Lesenswert?

Was für einen Ansatz suchst du denn? Du machst potentiell gefährliche 
Dinge, der Compiler warnt dich davor, alles schick.

Die Warnungen abzuschalten dürfte ja kein allzu großes Problem sein, 
oder?
https://stackoverflow.com/questions/3378560/how-to-disable-gcc-warnings-for-a-few-lines-of-code

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Klaus schrieb:
> der Compiler warnt dich davor,
Der Compiler warnt vor irgendwas, was ich da gar nicht tue.
Es gibt keinen Pointer, welcher auf ein Element auf dem Stack zeigt.

Klaus schrieb:
> Du machst potentiell gefährliche Dinge,
Welche?
https://en.cppreference.com/w/cpp/language/goto


Klaus schrieb:
> Die Warnungen abzuschalten dürfte ja kein allzu großes Problem sein,
> oder?
Doch!
> disable GCC warnings for a few lines of code
Ist in diesem Fall keine Lösung.
Es hat in einer Zeile zu geschehen.

Eine eher globale Abschaltung ist natürlich möglich, aber auch nicht so 
sinnvoll, da in der Luft hängende Pointer durchaus gefährlich werden 
können/sind.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Nimm Version 13, da ist die Warnung weg ;-)

von Andreas M. (amesser)


Lesenswert?

Arduino F. schrieb:
> Das Verfahren funktioniert.
> Mein Ziel ist es, es auf allen Arduino üblichen Plattformen (ohne
> Meldung) zum laufen zu bekommen.
> Bisher läuft es vom Tiny85(alle 8Bit AVR) über alle ESP bis gar zum K210
> mit seinen 64Bit.

Dein Verfahren funktioniert nicht oder zumindest nur für 
Spielzeuganwendungen. Schreibst du sogar selbst:

Arduino F. schrieb:
> Irrelevant, da keine Stacknutzung.

Arduino F. schrieb:
> Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste
> zu einer Kette von Vergleichen, welche es sehr langsam macht, ins
> Besondere für die weit hinten liegenden Fälle.

Das ist im allgemeinen so Unsinn. Es kommt auf den Compiler an.

Wieso verwendest Du Compilerspezifika, wenn du willst, das es auf 
möglichst vielen Platformen läuft? Das ist genau das Gegenteil von dem 
was man da macht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bei dieser nicht-standardisierten Erweiterung des computed-goto im GCC

https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

werden Labels wohl intern etwas anders behandelt. Anscheinend hat sich 
das mit Version 13 auch wieder geändert, zumindest ist die Warnung weg. 
Im Kontext von C++ solltest Du Dir auch über andere Folgen im Klaren 
sein.

Alles in allem: Mist!

von (prx) A. K. (prx)


Lesenswert?

Arduino F. schrieb:
> Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste
> zu einer Kette von Vergleichen

Die Entscheidungsfindung der Implementierung eines Switch-Statesments 
ist weit komplexer. Eine Variante der Kette aus Vergleichen ist ein 
Binärbaum an Stelle linearer Vergleiche. Bei vielen und weit auseinander 
liegenden Fällen ist so ein Entscheidungsbaum im Platzverbrauch 
effizienter als eine Tabelle. Weshalb das Optimierungsziel Tempo/Platz 
eine Rolle spielen kann.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Nimm Version 13, da ist die Warnung weg ;-)

Das wollte ich auch gerade schreiben.

Wenn ein Update aus irgendeinem Grund nicht möglich sein sollte:

Compiler-Warnungen sind Hinweise auf mögliche Fehler. Wenn du sicher
bist, alles richtig gemacht zu haben, und dich die Warnung stört, kannst
du sie ja auch einfach abschalten.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Andreas M. schrieb:
> Wieso verwendest Du Compilerspezifika, wenn du willst, das es auf
> möglichst vielen Platformen läuft? Das ist genau das Gegenteil von dem
> was man da macht.

In der Arduinowelt kommt bisher ausschließlich der Gcc zu Einsatz.

Andreas M. schrieb:
> Dein Verfahren funktioniert nicht oder zumindest nur für
> Spielzeuganwendungen. Schreibst du sogar selbst:
>
> Arduino F. schrieb:
>> Irrelevant, da keine Stacknutzung.

In dem Beispiel findet keine Stacknutzung statt, die mit dem 
Pointer/Warnung in Verbindung steht.
Natürlich nutzt das dort verwendete Serial.println() den Stack. Was aber 
kein Problem darstellt. Der Stack ist ja da und nutzbar.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Meinen herzlichen Dank für die freundliche Unterstützung.
Mit eurer Hilfe und noch ein bisschen Doku stöbern konnte ich mein 
Problemchen lösen.
Es funktioniert wie geplant, und die Meldungen unterbleiben.


Arduino F. schrieb:
> Warum ist taskLableStart keine lokale Variable (wirft keine Warnung)
> aber die anderen schon?
Dieses konnte hier leider nicht zu meiner Zufriedenheit beantwortet 
werden.

Arduino F. schrieb:
> Der Code funktioniert, nur die Meldungen nerven.
> Gibt es eine Abhilfe, ohne die dangling-pointer Warnung ganz
> abzuschalten?
Ja!

Siehe:
1
#define taskSwitch()                      \
2
  do {                                    \
3
    _Pragma("GCC diagnostic push")        \
4
    _Pragma("GCC diagnostic ignored \"-Wdangling-pointer\"") \
5
    lable = &&CONCAT(PREFIX,__LINE__);   \
6
    _Pragma("GCC diagnostic pop")        \
7
    return;                              \
8
    CONCAT(PREFIX,__LINE__): ;           \
9
    } while(0)

von Klaus (feelfree)


Lesenswert?

Arduino F. schrieb:
> Ist in diesem Fall keine Lösung.
> Es hat in einer Zeile zu geschehen.

Schön dass du doch noch selbst gemerkt hast, dass es in der Art eben 
doch geht.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Ja, finde ich auch!
> der Kopf ist rund, damit das denken die Richtung ändern kann.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Arduino F. schrieb:
> Es funktioniert wie geplant, und die Meldungen unterbleiben.

Für die typische Arduino-Definition von "funktioniert". Du springst 
immer noch in Bereiche ein ohne den Stack richtig zu handhaben. Und 
Register auch nicht.

Du hast ein triviales Beispiel dass zufällig funktioniert. Das  ist 
alles.

>> Der Code funktioniert, nur die Meldungen nerven.

Du kannst deine Behauptung noch so oft wiederholen, der Code 
funktioniert nicht. Hat vorher nicht funktioniert und wird durch 
Abschalten der Warnung auch nicht funktionieren.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Hannes J. schrieb:
> Du springst
> immer noch in Bereiche ein ohne den Stack richtig zu handhaben.
Es ist dem Protothread Konzept innewohnend, dass man lokale Variablen 
nur aufmerksam nutzen darf, da sie bei jedem return verfallen.

Hannes J. schrieb:
> Und Register auch nicht.
Für Registerinhalte gilt das gleiche!
Auch diese verfallen nach dem return.

Alles keine Überraschung!

Hannes J. schrieb:
> Du hast ein triviales Beispiel dass zufällig funktioniert.
Welches du natürlich belegen kannst.
Oder endet es bei der Behauptung?

Hier mal Lesestoff:
https://de.wikipedia.org/wiki/Protothread

: Bearbeitet durch User
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.