Hallo Konrad,
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Rekursion? *ins Buch guck* ahaa, bei mir rufen sich Funktionen selber auf. Gut zu wissen möcht ich dazu sagen. :-) Aber duie Funktionen rufen sich doch gegenseitig auf. also ruft main() physicmain() auf und weiter das main() main() aufruft habe ich net gefunden oder ist eben dieses aufrufen einer Funktion durch eine andere Rekursion?
Konrad
Ich habe die main.c mal mit hierrein geschrieben. Als Anlage wäre jene ja genau so gross oder?
< main.c >
/*************************************************************************** main.c - description ------------------- begin : Mon Mai 28 18:35:13 CEST 2001 copyright : (C) 2001 by Sebastian Roth email : sebbi@claranet.de ***************************************************************************/
/*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/
/*************************************************************************** * Corrections and advices by Konrad Rosenbaum konrad.rosenbaum@gmx.net * ***************************************************************************/
/*KR: Hinweis 1: Fragen weisen nicht immer auf Fehler hin, sondern oft auch auf Stellen, über die man einfach nachdenken sollte.*/
S: Ok, das kapiere ich
/*KR: warum denn config.h? Willst Du das Teil später via autoconf anpassen?*/
#ifdef HAVE_CONFIG_H #include <config.h> #endif
S: Hmm, das liegt daran, das ich das Prog mit KDevelop (1.4 en) erstellt S: habe. Da war das alles mit drin schon am Anfang. Es sah meiner Meinung S: nach nicht schlimm aus, weil ja in der config.h auch die Versionsnummer S: mit drinnesteht.
#include <stdio.h> #include <stdlib.h>
/*KR: warum static?*/ //static int eingabe; /*KR: ^^^^^^ das macht sich lokal besser als global! Grund: die Funktionen können sich dann nicht gegenseitig die Variablen verhunzen.*/ //static float v,s,t; /*KR: das gilt auch für diese. Noch ein Grund: wenn sie lokal sind wird der Speicher nur solange belegt, wie nötig. Das mag bei Größenordnungen von 16Byte (wie hier) noch lächerlich erscheinen, aber bei einigen hundert Funktionen mit ihrerseits dutzenden von Variablen, die teilweise im kilobyte-Bereich liegen fällt das schon ins Gewicht.*/
S: Nun ja, es war so. Ich wollte nicht immer wieder in jeder Funktion, in S: der man etwas auswählen kann immer wieder von vorn eine Variable S: definieren, die das repräsentiert, was man eben ausgewählt hat (1,2,3...). S: Die Variable hatte ja nix weiters zu bedeuten, sollte nur den Eingabewert S : darstellen. Bei den anderen (s.v,t) muss ich dir wohl Recht geben.
/*KR: Funktionen, die schon genutzt aber erst später definiert (=implementiert=ausprogrammiert) werden, sollten schonmal vorher deklariert (=bekanntgegeben) werden. Man nennt das Forward-Deklaration. Und genau das tun übrigens auch die meisten Header-Dateien, wie stdio.h*/
S: Hmm, da habe ich in dem Buch gelesen, das man die Funktionen vor die main S: schreiben soll, da brauch man sie auch nicht deklarieren. Da werde ich S: aber nochmal genau gucken
void physicmain();/*KR: Unterschied: statt {...} kommt ein Semikolon ; */ void geschwzeitausw(); void wegzeitgleich(); void wegzeitngleich(); void main2();
S: Ok bis jetzt.
int main(int argc, char *argv[]) { /*KR: der viele printf und switch-Code muss wirklich nur 1x existieren, also lagern wir ihn in eine Funktion aus (main2).*/ /*KR: Hauptschleife (sowas haben alle Ereignisorientierten Programme - Du bist in bester Gesellschaft.*/ for(;;)main2();
S: Ja, das verstehe ich. Die zweite main hatte ich deswegen genommen, weil S: der User, wenn er zurück ins Hauptmenü will, nicht erst wieder den S: Begrüßungstext sehen soll. Das hat mich genervt, wusste aber nicht, wie S: man es sonst anders machen könnte.
/*KR: Du kommst jetzt in den Genuß meiner Endlosschleifen-sammlung. Besser lesbar wäre sicherlich die gekommen: while(1) main2(); noch besser: while(1){ main2(); } andererseits ginge auch das: do{ main2(); }while(1);
S: aha! :-)
Oder etwas esotherischer: int i; for(i=1;i==1;i=1){ main2(); } was i auf 1 initialisiert, es auf 1 prüft und es wieder und wieder auf 1 setzt. Etwas redundant, also lassen wir das zweite setzen weg. Und die {} auch. ;-) int i; for(i=1;i==1;) main2(); wozu eigentlich i auf 1 prüfen? das Ergebnis des Ausdrucks ist schließlich wieder 1! int i; for(i=1;1;) main2(); wozu noch i definieren und setzen? for(;1;) main2(); also eigentlich sollte er wissen, was ich will!! for(;;) main2();
S: Wenn du mich damit meinst. Ja das habe ich gerafft. :-)
quod erat demonstrantum!
S: Hat jemand ein Latein-Deutsch(Sächsisch) Wörterbuch. S: Vermutung q.e.d Was zu beweisen war? Aus Mathematik?
*/ }/*Ende der Hauptfunktion*/
void main2(void) { int eingabe;/*KR: was sach' ich? Ist besser! ;-) */
/*KR: netter Sprachmix. Entscheide Dich bitte für eine Sprache. ;-) */
S: Ja da hast du schon wieder Recht. Also werde ich mich für tatatataa S: Deutsch entscheiden :-)
printf("Willkommen bei Physics for Linux!\n\n"); printf("Hauptmenü:\n"); printf("Eine physikalische Gleichung errechnen --> 1\n"); printf("Hilfe --> 2\n"); printf("Beenden --> 3\n");
/*Jetzt muss der User auswählen*/
printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { /*KR: es ist i.A. übersichtlicher, wenn die öffnende { hinter oder unter switch steht und der ":" hinter dem case xy und das darunter eingerückt wird....*/
S: Ok stimmt schon wieder. Sieht evtl. auch besser aus. Damals hatte ich ein S : wenig experimentiert mit dem Code(aussehen)... weniger erfolgreich , wie S : sich fetstellen lässt
/*KR: WICHTIG: bitte bitte lies Dir die Torvalds-Styles durch: /usr/src/linux/Documentation/CodingStyle */
S: *gefunden* *zumlesenvorgemerkt*
case 1: printf("Jetzt geht zur Physik!"); physicmain(); break;
case 2: printf("Noch nicht fertig"); break;
case 3: printf("cu...\n"); /*KR: bitte auch bei Funktionsnamen für exakt eine (1) Sprache entscheiden - möglichst die selbe, wie in den Strings.*/
S: Ok, nochmal Deutsch
//beenden(); /*KR: beenden ist recht kurz, also kann man es auch gleich von hier aus ausführen*/
S: Ja ok, stimmt.
exit(EXIT_SUCCESS); break;
default: printf("Bitte eine Zahl auswählen!\n"); /*KR: ohne extra Aufruf kehrt es zu main() zurück das geht in eine Schleife, die wieder main2 aufruft ------ das spart uns eine Menge Stack! Der Aufruf von main2 an dieser Stelle bringt Dich in eine Endlosschleife, die jedesmal Speicher verbraucht, bis Du beendest.*/
S: *insbuchguckundbegriffstacksuch* Ist mit Stack ein Teil des Speichers S: gemeint? Sorry, Beschreibung kenn ich nicht
//main2(); break; }/*Switch beenden*/ }/*Ende der Neben-Hauptfunktion*/
/*KR: wird nicht mehr gebraucht, siehe main2 void beenden(void) // Zuletzt editiert am 29.05.01 { exit(EXIT_SUCCESS); }/*Programm beenden*/
S: Gebongt! Weg damit..
void physicmain(void) // Zuletzt editiert am 30.05.01 { int end=0;/*KR: unterscheidet zwischen Ende und Schleife*/ int eingabe; do{ /*Da Rekursion (=Selbstaufruf) immer Ressourcen verbraucht ist eine Iteration (=Schleife) immer vorzuziehen, wenn der Code dadurch nicht übermäßig schwieriger wird. Erst recht, wenn er (wie hier) übersichtlicher wird.*/
S: gut printf("\nMit welcher Gleichung, welchem Gesetz soll gerechnet werden?\n"); printf("Weg-Zeit Gesetz --> 1\n"); printf("Beenden... --> 2\n"); printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { case 1: geschwzeitausw(); break;
case 2: /*KR: die selbe Kritik, wie in main2: ein einfaches beenden bringt uns in die Schleife zurück... ...versuch Dir mal zu überlegen, was passiert, wenn...*/ //main2(); end=1; break; default: printf("\nBitte eine gültige Zahl eingeben!\n"); /*die Schleife geht weiter*/ break; } }while(!end);/*KR: mach weiter, bis der Nutzer beenden will*/ /*KR: Hinweis "!end" heißt "not end" also ergibt dieser Ausdruck 1 (wahr, true), wenn end gleich false (in C: 0); ist end irgendwas ungleich 0 (< oder > 0) dann ergibt der Ausdruck 0 (false)*/
S: Ja das lässt sich verstehen.
}
void geschwzeitausw(void) { int eingabe; printf("Gleichförmig --> 1\n"); printf("Ungleichförmig --> 2\n"); printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { case 1: wegzeitgleich(); break; case 2: wegzeitngleich(); break;
default: printf("Bitte gültige Zahl eingeben"); break; } }
void wegzeitgleich(void) { /*KR: Insider Tipp: Einzel-chars machen sich mit scanf echt blöd. Also: String einlesen (a'ka ganze Zeile) und dann nur den ersten Character nehmen. Noch so ein Tipp: ein Array kann auch wie ein Pointer behandelt werden (und umgekehrt) die Ausdrücke x[0] und *x sind also immer identisch.*/
S: Erklär mir bitte das x[0] nochmal! Mir Zeigern und Vektoren komme ich bis S : jetzt noch net so recht aus. das *x ein Zeiger auf x ist, verstehe ich.
/*KR: mehr als 100 wird er wohl net tippen, auch wenn diese Schätzung noch riskant ist...*/
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
char gvar[100]; float s,v,t;
printf("Grundgleichung: s=v*t\n"); printf("Bitte die gesuchte Variable eingeben:"); scanf("%s",gvar);/*KR: Strings ohne & übergeben, sie sind schon pointer*/
S: Ok (schonwieder)
switch(*gvar)/*KR: das erste Element nehmen*/ { case 's': printf("Gesucht ist > s <\n"); printf("Bitte einen Wert für v (in m/s) eingeben: "); scanf("%f",&v); printf("Bitte einen Wert für t (in s) eingeben: "); scanf("%f",&t); s = v * t; printf("Geschwindigkeit: %f m/s\n",v); printf("Zeit: %f s\n",t); printf("Weg: %f m\n",s); /*KR: wie "böse", wir kommen bei Rücksprung doch sowieso dorthin zurück!*/
S: *an alle* Sorry, wenn der Quelltext tödlich für einige zu lesen ist. Bin S: C-Anfänger
//physicmain(); break; default: /*KR: man sollte immer Fehler und Fehleingaben behandeln*/
S: Stimmt printf("Sorry, andere Werte als 's' sind noch nicht implementiert.\n"); break;/*KR: break sollte man auch immer machen, Fehler passieren zu schnell!*/
S: Ok. } }
void wegzeitngleich(void) { }
</ main.c >
Also, ich danke dir erstmal ganz toll. Mal sehen, ob alles so funktioniert wie es soll. *1Minute später* Es klappt. Ich werd verrückt. Juhuu! Es war sehr nett von dir, das du Zeit gefunden hast für dieses Problem, jetzt bin ich um einiges schlauer geworden in Sachen C-Programmierung. Noch eine Frage: Welche Kommentare kann man drinne lassen im Quelltext? Wird das Prog zu groß mit Kommentaren?
Ebenfalls auch ein großes Danke an Christian für seinen Tipp mit scanf. Jene Funktion werde ich auch noch ausprobieren.
Ok, das war´s erstmal von mir. Machts gut.
Bye, Sebastian