Hallo zusammen!
Ich komme hier grad nicht weiter, vielleicht weiß ja jemand einen Rat.
Ich bekomme per UART einen Befehl rein in ASCII-Form. Jetzt versuche ich
gerade eine Kommandoauswertung zu machen, also habe ich ein Array:
1
#define NR_OF_COMMANDS 3
2
3
char*cmd_list[]={"TYPE?","ID?","MOMVALUE"};
Jetzt will ich herausfinden, welches Kommando angekommen ist, bzw. ob es
ein gültiges ist, also mache folgendes:
1
int8_tcommand_interpreter(void)
2
{
3
uint8_tcounter;
4
5
for(counter=0;counter<NR_OF_COMMANDS;counter++)
6
{
7
if(!uart_compare_rx_string(cmd_list[counter]))
8
{
9
returncounter;
10
}
11
}
12
13
return-1;
14
}
Diese Funktion sendet also den Textstring aus cmd_list, abhängig vom
Zählindex counter an die Unterfunktion uart_compare_rx_string, welche so
aussieht:
Das Problem jetzt: Das ganze funktioniert wunderbar, solange man nur die
Befehle TYPE? und ID? benutzt - das kann man im Wechsel beliebig oft hin
und her machen. MOMVALUE? geht genau einmal, danach ist beim
String-Compare für jeden String die Stringlänge immer 0.
Hat jemand einen Ansatz wo ich da suchen sollte?
Du castest da implizit rum, würde ich behaupten (uint8_t stringlength
und sowas), also mal alle Warnungen anmachen, dann sollte der Compiler
schon ein wenig meckern.
Als allgemeine Empfehlung noch ein NUL-terminiertes Array zu benutzen
statt der fehleranfälligen Konstruktion von Array + Array-Länge.
Ergo (immer alles const machen was const machbar ist!)
const char *cmd_list[] = {"12", "123", 0};
Iteration
int i = 0;
while(cmd_list[i]) {
// was mit cmd_list[i] machen
i++;
}
Geht natürlich auch als for-Schleife
Von der Programmstruktur her empfinde ich eine Funktion wie
uart_compare_rx_string als groben Pfusch; besser: Standardfunktionen
benutzen und sowas wie uart_rx_get_string haben, dann ist sofort klar,
mit welchen Daten gearbeitet wird. Außerdem duplizierst du nicht etliche
Stringvergleichsfunktionen, nur weil jeweils der eine Operand ein
anderer ist.
Hans schrieb:> Hat jemand einen Ansatz wo ich da suchen sollte?
So wie das geschrieben ist: auf jeden Fall nicht in dieser Funktion. Das
Problem ist wo anders.
PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar
nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit
nicht existiert. Die Funktion ist genauso sicher bzw. unsicher, wie wenn
du einfach nur einen strcmp benutzt hättest.
Karl Heinz schrieb:> PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar> nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit> nicht existiert.
Stimmt, das habe ich ja gar nicht gesehen.
Marian B. schrieb:> Du castest da implizit rum, würde ich behaupten (uint8_t stringlength> und sowas)
Sorry, das verstehe ich nicht, das sind doch nur Variablen damit ich
sehen kann, was als Ergebnis aus strlen herauskommt und was strcmp mir
zurückgibt. Welchen cast meinst du?
Marian B. schrieb:> besser: Standardfunktionen> benutzen
Was ist denn hierfür die Standardfunktion?
Marian B. schrieb:> Du castest da implizit rum, würde ich behaupten (uint8_t stringlength> und sowas), also mal alle Warnungen anmachen, dann sollte der Compiler> schon ein wenig meckern.
Da gibts keine Warnungen (GCC mit -Wall, -Wextra und -pedantic). Klar
ist es schöner, size_t zu verwenden. Aber auf einem uC darf man durchaus
kleinere Datentypen verwenden.
Marian B. schrieb:> Ergo (immer alles const machen was const machbar ist!)> const char *cmd_list[] = {"12", "123", 0};
Na dann auch wirklich alles:
Karl Heinz schrieb:> Hans schrieb:>>> Hat jemand einen Ansatz wo ich da suchen sollte?>> So wie das geschrieben ist: auf jeden Fall nicht in dieser Funktion. Das> Problem ist wo anders.>> PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar> nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit> nicht existiert. Die Funktion ist genauso sicher bzw. unsicher, wie wenn> du einfach nur einen strcmp benutzt hättest.
Und nachdem sie dann auf
1
int8_tuart_compare_rx_string(char*test_string)
2
{
3
returnstrcmp(uart_control.rx_buffer,test_string);
4
}
eindampft, kannst du dir den ganzen Funktionsaufruf auch sparen, indem
du im Commando-Interpreter den strcmp machst
und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer
verlagerst
1
int8_tcommand_interpreter(constchar*command)
2
{
3
uint8_tcounter;
4
5
for(counter=0;counter<NR_OF_COMMANDS;counter++)
6
{
7
if(!strcmp(command,cmd_list[counter]))
8
{
9
returncounter;
10
}
11
}
12
13
return-1;
14
}
dann hast du eine schöne Funktion, der du ein Kommando übergeben kannst,
und die dann das weitere veranlasst. Egal wo das Kommando herkommt. Sei
es von der UART oder sei es, dass du im Programm selber diesen
Interpreter mit einem konstanten String aufrufst.
strlen liefert nach POSIX size_t, was z.B. beim AVR-GCC uint16_t
entspricht und uint16_t =/= uint8_t. Ergo impliziter Cast bei der
Zuweisung. strcmp liefert nach POSIX int, was z.B. beim AVR-GCC int16_t
entspricht und int16_t =/= int8_t. Wenn jemand solchen Code schreibt,
liegt der Verdacht nahe, dass es an allen anderen Stellen ähnlich
aussieht und eine alte Weisheit sagt, dass implizite Casts implizite
Fehler implizieren.
be stucki schrieb:> Na dann auch wirklich alles:const char * const cmd_list[] = {"12",> "123", 0}
Sehr richtig :-)
Full Ack.
Vor allen Dingen dieses const machen würde ich sehr empfehlen. Und dann
natürlich auch an allen verwendenden Stellen durchziehen. Aber das sagt
dir dann schon der Compiler, bei welchen Funktionsargumenten das ein
oder andere const fehlt. Die Einführung von const zieht zwar oft einen
Rattenschwanz an (einfachen) Änderungen bzw. Anpassungen nach sich. Die
sind es aber wert gemacht zu werden.
Ob man da mit einer impliziten Längenangabe arbeitet, oder mit einem
#define in dem die Längenangabe steht, oder mit einem #define, welches
die Länge mittels sizeof bestimmt, würde ich momentan erst mal als nicht
ganz so wichtig abtun. OK, mit dem 0 Pointer im Array spart man sich
Funktionsargumente.
Der Vorteil eines terminierten Arrays ist einfach der, dass man nicht
vergessen kann die Längenangabe anzupassen, eine Fehlerquelle weniger.
Wenn man vergisst die Längenangabe um eins zu erhöhen, nachdem man einen
neuen Befehl hinzugefügt hat, kann man lange Suchen, wieso der Befehl
nicht funktioniert...
Wo wir gerade bei "alles const machen was geht" sind: Hilfsfunktionen
und modulweite globale Variablen static machen, cmd_list ist da
wahrscheinlich auch ein Kandidat.
Marian B. schrieb:> Wenn man vergisst die Längenangabe um eins zu erhöhen, nachdem man einen> neuen Befehl hinzugefügt hat, kann man lange Suchen, wieso der Befehl> nicht funktioniert...
Na ja
Man macht das natürlich ja auch nicht so
in der ich mich um das #define überhaupt nicht mehr kümmern muss, wenn
ich neue Strings hinzufüge.
Aber darüber will ich mich jetzt nicht streiten. Man kann es so oder so
machen. Letzten Endes läuft es darauf hinaus, ob man in allen
Funktionen, die dieses Array als Argument kriegen, auch die Länge
mitgeben muss oder ob man die aus dem Array ermitteln kann. Letzters ist
sicherlich schöner, aber so ein großes Problem ist es dann auch wieder
nicht.
Tatsache ist aber, dass das Problem nicht auf den geposteten Code
zurückgeführt werden kann.
Also verfolge mal, was zb mit dem Rückgabewert der Funktion gemacht
wird. Irgendwo wirst du dir ins Array cmd_list reinschreiben. Entweder
indem du unmitoviert da einen Pointer auf einen tatsächlich leeren
String reinschreibst, oder indem du gleich einen NULL Pointer ins Array
schreibst.
Gerade deshalb wäre das durchziehen von const nicht ganz unwichtig. Denn
dann klopft dir der Compiler da auf die Finger, wenn du das bewusst
versuchst. Kann natürlich auch sein, dass du wanders ein Array
überläufst und dabei zufällig genau diese cmd_list erwischt hast. const
alleine ist auch kein Allheilmittel. Aber es hilft.
Da melde ich mich jetzt nochmal zurück.
Also funktionieren tut es jetzt. Aber nun würde ich nochmal gerne den
besten Weg für die ganze Geschichte finden.
Es sieht folgendermaßen aus:
Ich habe eine Funktion uart_functions.c / .h, in der auch mein
uart_control.rx_buffer steckt. Dieses Array ist mit volatile deklariert,
da dieses Array per UART-Interrupt gefüllt wird. An dieser Stelle
meckert der Compiler z.B. schonmal, dass "volatile char" inkompatibel
mit dem "const char" des StringCompare ist. Aber wie sollte ich das
jetzt am besten lösen? Ich kann ja den rx_buffer nicht const machen, da
er sich ja ändert, oder wo ist hier mein Denkfehler?
Der rx_buffer ist ja nur in der uart_functions bekannt, daher habe ich
die Vergleichsfunktion auch in den uart_functions.
Karl Heinz schrieb:> und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer> verlagerst
Wie soll ich das verstehen? Was packe ich denn jetzt am besten wohin?
Wäre es besser, uart_control global zugänglich zu machen?
Hans schrieb:> Ich habe eine Funktion uart_functions.c / .h, in der auch mein> uart_control.rx_buffer steckt. Dieses Array ist mit volatile deklariert,> da dieses Array per UART-Interrupt gefüllt wird. An dieser Stelle> meckert der Compiler z.B. schonmal, dass "volatile char" inkompatibel> mit dem "const char" des StringCompare ist.
An dieser Stelle könnte man das volatile wegcasten, da der Compiler wohl
sowieso keine Chance hat, da durch Optimierung etwas zu vermasseln.
> Aber wie sollte ich das> jetzt am besten lösen? Ich kann ja den rx_buffer nicht const machen, da> er sich ja ändert, oder wo ist hier mein Denkfehler?
Den willst du auch nicht const machen.
Das const an dieser Stelle:
> int8_t uart_compare_rx_string( const char *test_string )
ist die Zusicherung der Funktion:
Ich werde mich nicht an den Daten vergreifen. Ich behandle deinen
String, wie wenn er konstant wäre. D.h. ich lese den String aus, benutze
ihn um irgendwelche Entscheidungen zu treffen, aber ich werde nicht
versuchen, den String zu verändern.
Das ist die Bedeutung eines const in der Argumentliste einer Funktion.
Das hat erst mal nichts damit zu tun, ob die entsprechende Variable vom
Aufrufer const ist oder nicht. Für beides gilt: wenn eine aufgerufene
Funktion diese Zusicherung gibt, dann bin ich auf der sicheren Seite und
muss nicht damit rechnen, dass die Funktion den String verändert.
Aus genau dem gleichen Grund, ist ja zb das Argument der Funktion strlen
(welches die Länge eines Strings bestimmt)
1
size_tstrlen(constchar*str)
das bedeutet nicht, dass man da nur konstante Strings reinstecken darf,
wie in
1
i=strlen("Hallo World");
sondern man darf selbstverständlich auch variable Strings reingeben
1
charstr[40];
2
strcpy(str,"Hallo World");
3
4
...
5
6
i=strlen(str);
während es bei letzterem noch egal wäre, wenn die Funktion den String
selbst verändert (ist ja ein Array im Speicher), wäre das in diesem Fall
1
i=strlen("Hallo World");
fatal. Das String Literal "Hallo World" ist für das Programm tabu. Es
ist illegal, zu versuchen dieses Literal zu verändern (zb durch eine
Zuweisung von Einzelzeichen. Daher ist es in diesem Fall wichtig, dass
strlen
1
size_tstrlen(constchar*str)
mit seinem const die Zusicherung gibt: Ich versuche das erst gar nicht.
Mach dir keine Sorgen, ich lasse den String, den du mir gibst, in Ruhe!
Das const bewirkt, dass in so einem Fall
1
voidfoo(constchar*str)
2
{
3
str[0]='a';
4
}
der Compiler aktiv wird: Die Funktion hat die Zusicherung gegeben, den
Inhalt von str nicht zu verändern und trotzdem versucht sie es mit der
Zuweisung. Das kann aber nicht sein! Zusicherung ist Zusicherung und
damit ist dann die Zuweisung ein Fehler, der auch vom Compiler angemerkt
wird.
Ein const bewirkt also 2 Dinge: zum einen ist es ein Kontrakt mit dem
Aufrufer, was die Funktion tun wird bzw. nicht tun wird. Und zum anderen
überwacht der Compiler auch, ob sich die Funktion an diese Zusicherung
hält.
> Der rx_buffer ist ja nur in der uart_functions bekannt, daher habe ich> die Vergleichsfunktion auch in den uart_functions.
Du musst dir über Zuständigkeiten im klaren werden!
das rx-Modul ist dafür zuständig, die UART zu behandeln. Das UART-Modul
ist aber nicht dafür zuständig, einen Commando Interpreter zu bedienen.
Das gehört nicht in seinen Kompetenzbereich.
Genauso, wie du immer ein wenig abseits deines aktuellen Projektes über
den Tellerrand schauen solltest. In deinem konkreten Programm willst du
den Kommando-Interpreter auf den Strings arbeiten lassen, die du per
UART bekommst. Aber ist das immer so? Oder ist es nicht eigentlich so,
dass es für einen Kommando-Interpreter völlig egal sein sollte, wo der
String herkommt, mit dem er arbeitet?
Ich würde mal letzteres sagen. Für einen Kommando Interpreter sollte das
soch eigentlich keinen Unterschied machen. Der kriegt einen String und
den wertet er aus. Ob der String von einem File auf einer SD-Karte
kommt, ob der per Netzwerkverbindung eingetroffen ist, ob der per UART
gekommen ist oder ob den ein Benutzer über eine Tastatur eingegeben hat,
das sollte doch für einen Kommando-Interpreter keinen Unterschied
machen. Er bekommt einen String und den wertet er aus - egal wo der
String hergekommen ist.
Daher sollte ein Kommando-Interpreter überhaupt nicht wissen, wo der
String herstammt.
Natürlich wird man auf einem kleinen µC auch schon mal eine Grenze
ziehen und zulassen, das die reine Lehre auch schon mal etwas verwässert
wird. Je nachdem welchen Mehraufwand man sich einhandelt. Aber ein
1
intmain()
2
{
3
...
4
5
while(1){
6
if(uart_string_available())
7
{
8
cmdNumber=command_interpreter(uart_rx_string());
9
....
10
}
(oder wie auch immer du dann die Funktionen nennst)
ist jetzt dann auch wieder nicht sooooo viel Mehrarbeit, dass man davor
zurückscheuen muss um die Zuständigkeiten zu erahlten: Die UART ist
dafür zuständig einen String zu empfangen, der command Interpreter ist
dafür zuständig einen String auszuwerten und das verknüpfende Bindeglied
sitzt in main(), welches die Daten von dem einen Modul in das andere
Modul entsprechend den Programmanforderungen und der geforderten Logik
weiterleitet. Es gibt kaum einen triftigen Grund, dafür Abängigkeiten
zwischen 2 Modulen in den Module selbst zuzulassen.
>> und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer>> verlagerst>> Wie soll ich das verstehen?
Es gibt keinen Grund, warum in der main() nicht das Bindeglied zwischen
den Welten 'UART' und 'Kommando-Auswertung' sein soll.
> Was packe ich denn jetzt am besten wohin?> Wäre es besser, uart_control global zugänglich zu machen?
Du kannst auch eine Funktion machen, die dir Zugang zum String gibt.
Wenn du eine Funktion hast, die dir einen anderen String mit dem UART
String vergleicht, dann kannst du den UART String auch gleich
rausrücken. Das schenkt sich nicht. Oder auf einem µC (auch wenn es
gegen die reine Lehre verstößt), direkten Zugang zum Array gewähren, in
welchem der String steht.
Hi Karl Heinz!
Danke für deine sehr ausführlichen Antworten. Um es jetzt mal endgültig
zu verstehen...ich konnte nämlich herausfinden, ab wann das Programm
nicht mehr rund läuft in meiner alten Variante. Es liegt hier auch am
"const" - so sah es vorher aus:
uart_functions.c
Und jedesmal, wenn die sprintf-Anweisung ausgeführt wurde, war das Array
in uart_functions.c platt. Wenn ich es jetzt ebenfalls als "const char *
const uart_err_msg_array" anlege, dann geht es.
Aber wieso passiert das an der Stelle? Kann der Compiler da einfach
drüberschreiben? Bzw. wieso schreibt er einfach drüber?
Ach und nochwas: Ist es richtig, dass mir strncmp auch Werte ausgibt,
die nicht -1, 1 oder 0 sind. Ich habe nämlich auch Zahlen wie 18 als
Rückgabewert. Nur überall im Netz lese ich nur von den drei Zahlen.
Hans schrieb:> Nur überall im Netz lese ich nur von den drei Zahlen.
merkwürdig. Im ersten Link im netzt steht:
Returns an integral value indicating the relationship between the
strings:
A zero value indicates that the characters compared in both strings
form the same string.
A value greater than zero indicates that the first character that does
not match has a greater value in str1 than in str2; And a value less
than zero indicates the opposite.
Karl Heinz schrieb:> int main()> {> ...>> while( 1 ) {> if( uart_string_available() )> {> cmdNumber = command_interpreter( uart_rx_string() );> ....> }
Das sieht besser aus, aber was macht die Funktion uart_rx_string() jetzt
genau? Sie muss doch in dem Fall die Adresse des Arrays zurückgeben,
oder? Wie sieht die Funktion denn dann aus?
char *uart_rx_string( void )
?
> Und jedesmal, wenn die sprintf-Anweisung ausgeführt wurde, war das Array> in uart_functions.c platt.
Ist interessant, daa %E eigentlich mit Floating Point werten umgehen
können müsste. Probier mal ein ganz normales %f, nicht dass da die
Implementierung für %E fehlerhaft ist.
> Wenn ich es jetzt ebenfalls als "const char *> const uart_err_msg_array" anlege, dann geht es.
Wird wahrscheinlich einfach nur an einer anderen Stelle im Speicher
platziert sein. Damit trifft der 'Fehler' jetzt irgendwas anderes im
Speicher, was er platt macht.
> Aber wieso passiert das an der Stelle? Kann der Compiler da einfach> drüberschreiben? Bzw. wieso schreibt er einfach drüber?
Der Compiler schreibt da überhaupt nicht drüber. Das Programm tut es.
Und es gibt viele, viele Möglichkeiten was da alles passiert sein kann.
Ohne den Code für printf zu sehen, ist das aus der Entfernung recht
schwer einzuschätzen, was da konkret passiert.
Hans schrieb:> Also funktionieren tut das hier folgendermaßen:>
1
>constchar*uart_return_rx_buffer_address(void)
2
>{
3
>return(constchar*)uart_control.rx_buffer;
4
>}
5
>
> Nur ist es auch richtig so?
Ist perfekt.
Nur den Cast kannst du weglassen.
EIn derartiges const im Datentyp des Returnwertes bedeutet hier einfach
nur:
Lieber Aufrufer! Ich gebe dir zwar jetzt einen Pointer auf ein
Speicherfeld, aber du darfst diesen Pointer nicht dazu benutzen, mir den
String unter dem Allerwertesten zu verändern. Für dich, geschätzter
Aufrufer, ist dieser String tabu und darfst ihn nicht ändern.
Du kannst daher diesen Pointer in allen Operationen benutzen, die die
Zusicherung geben, den String nicht zu verändern
1
constchar*pStr=uart_return_rx_buffer_address();
2
size_tlen=strlen(pStr);// Ist ok, denn strlen gibt die Zusicherung
3
// den String nicht zu verändern
du kannst ihn aber nicht dazu benutzen, den String zu verändern
1
constchar*pStr=uart_return_rx_buffer_address();
2
pStr[0]='a';// Fehler. pStr zeigt auf einen konstanten String
3
strcpy(pStr,"Hallo");// Fehler: der ganze String in pStr ist nicht änderbar
4
5
chartmp[40];
6
strcpy(tmp,pStr);// Ist soweit ok. strcpy gibt die Zusicherung, die Quelle nicht zu verändern
Du castest mir da ein wenig zu sorglos rum ohne dir zu überlegen, was
der Datentyp und insbesondere die Modifier wie 'const' eigentlich
aussagen. Bei const dreht sich alles darum, schreibende Zugriffe
einzuschränken. Und selbstverständlich kann eine Funktion
1
staticchartext[50];
2
constchar*getPtr(void)
3
{
4
returntext;
5
}
dem Aufrufer einen Pointer auf ein char-Array als Pointer auf const
rausgeben, um den Aufrufer zu signalisieren: Hände weg von meinem Array!
Du darfst gerne lesend zugreifen, aber verändern darfst du den Inhalt
nicht. In diesem Sinne ist das einfach nur eine Einschränkung, die beim
Liefern der Adresse dazukommt. Und dazu braucht es keinen Cast. Die
Rechte des Aufrufers einzuschränken kann niemals problematisch sein.
Umgekehrt aber schon. Wenn sich der Aufrufer einen Dreck um diese
Einschränkung kümmert ...
1
char*pStr=(char*)uart_return_rx_buffer_address();
2
pStr[0]='a';// Fehler. pStr zeigt auf einen konstanten String
... und das const einfach wegcastet, dann kann (muss aber nicht) das
alles problematisch werden.
Casts sind Waffen!
Die will man nicht nach der Rundumschlagmethode nach dem
Gieskannenprinzip verteilen.
Ich heul gleich...
Karl Heinz schrieb:> Ist interessant, daa %E eigentlich mit Floating Point werten umgehen> können müsste. Probier mal ein ganz normales %f, nicht dass da die> Implementierung für %E fehlerhaft ist.
Es geht weiter mit dem sprintf...ich weiß echt nicht mehr weiter!!!
1
output_value=100.0;// GLOBALE VARIABLE
2
3
4
voidmain(void)
5
{
6
...
7
charuart_response[30];
8
floatout_val=400.35;
9
uint8_tbytecount;
10
11
bytecount=sprintf(uart_response,"%.2f",out_val);
12
}
Ich habe eine globale Variable "output_value", welche ich eigentlich mit
dem sprintf ausgeben möchte. Wenn ich dies tue, dann ist output_value
jedesmal nach dem sprintf wieder auf 0.0 - testweise habe ich nun
"out_val" im sprintf. Das funktioniert auch, out_val behält ihren
Inhalt, aber die globale "output_value"-Variable wird weiterhin
verändert durch das sprintf, obwohl auf diese garnicht zugegriffen wird.
Bitte bitte sagt mir, wo ich suchen kann...ich verzweifel hier wirklich.
Der Controller startet neu...:-\ Das sprintf resettet den - sehe ich
jetzt an einer LED welche ich mit einem ersten Kommando ausschalte und
die beim Start dann wieder angeht.
Hans schrieb:> Es geht weiter mit dem sprintf...ich weiß echt nicht mehr weiter!!!
Natürlich geht das weiter. Du hast ja bisher das Problem nicht gelöst.
Dein Programm überschreibt den Speicher für globale Variablen. Hat es
früher getan, und tut es immer noch. Deine String-Konstanten sind durch
das const an eine andere Stelle gewandert, damit trifft es jetzt andere
Variable.
Auf und für was programmierst du eigentlich? Stack groß genug?
Oliver
Hans schrieb:> Ohne den Cast meckert der Compiler, dass die Datentypen nicht passen.
Ja.
Aber er meckert nicht wegen dem const.
Sondern er meckert, weil du an dieser Stelle das 'volatile' verlierst.
Abgesehen davon:
Ich schliesse mich an. Es sieht wohl so aus, als ob dein Programm zu
groß für den Stack ist bzw. zu intensive Stacknutzung betreibt.