Erstes Programm: "Laufzeit"fehler und Projektidee



  • Aus Fehlern lernt man aber mehr als aus von vornherein richtig geschriebenen C-Programmcode. Ich habe ein Buch über C++, in dem C-Zeiger und vor allem -Arrays nur ganz am Rande abgehandelt werden. Ich erachte es für sinnvoll, zuerst die C-Syntax zu erlernen (und typedef struct enum (union) * & [] etc. pp.), bevor man Klassen und Objekte in C++ kennenlernt (und auch eine teilweise andere Syntax). Also C -> C++ und nicht C++ AND C. Wenn ich die 31-32 Zeilen aus den vorherigen Beiträgen zum Laufen bringen würde, wäre mir sehr geholfen. Ich habe in der Vergangenheit auch Fragen über Java beantwortet und dachte, eine ähnliche Community-struktur gäbe es auch hier. Ich will mir gar nicht Lösungen erbetteln, aber für jmd., der schon 1-2 Jahre in C programmiert hätte, wäre das doch ein Kleinigkeit....


  • Mod

    regInfo000? schrieb:

    aber für jmd., der schon 1-2 Jahre in C programmiert hätte, wäre das doch ein Kleinigkeit....

    Nein. Dein Fragenkatalog stellt vom Umfang her, wollte man alles beantworten, so manches schlechte Lehrbuch in den Schatten.

    Bezüglich C und C++: Die allgemeine Empfehlung ist, dringend C++ zu lernen, wenn man C++ machen willst. Die Syntax wird schließlich mit der Sprache gelernt. Selbst wenn sie hier ausnahmsweise gleich ist zur Syntax einer anderen Sprache, so ist das doch kein Argument, die andere Sprache zu lernen. Wenn du Java kennst, dann kennst du die Syntax von C und C++ ohnehin schon.

    Was du aber erreichst, wenn du stattdessen eine andere Sprache lernst, ist, dass du dann die Spezialitäten und Herangehensweisen dieser anderen Sprachen lernst. In C++ programmiert man jedoch ganz anders als in C oder Java, auch wenn von der Syntax her alles gleich aussieht. Du musst lernen, wie man in C++ vorgeht und das lernst du nur, wenn du C++ lernst. Es funktioniert als Anfänger sogar eine Weile, C/Java in C++ zu machen, aber weder nutzt man die Stärken und Möglichkeiten der Sprache, noch ist das Ergebnis wirklich im Sinne des Erfinders. Das sind später die schlimmsten Programmierer, die hier Fragen zu den schlimmsten Hackcodes stellen.

    C ist keine vereinfachte Version von C++! C++ ist keine einfache Erweiterung von C*! Es sind komplett unterschiedliche Sprachen mit gleicher Grammatik.

    *: Das mag historisch so sein, aber trifft nicht den Kern der Sache.



  • Dein Programm ist so voller Fehler, das es eher eine Klausuraufgabe ist "Finden sie alle Fehler und verbessern Sie diese"

    Wenn du im K&R mal nach strcat oder strcpy schaust (da ist ein Beispiel drin), weißt du auch schon wie es richtig geht. Bzw. sollten dann nur ca. zwei Fehler übrig bleiben.

    Da helfen wir auch gerne.



  • DirkB schrieb:

    , das es eher eine Klausuraufgabe ist "Finden sie alle Fehler und verbessern Sie diese"

    Dann aber auch fies sein und // einfügen und */ am eNde des Kommentars nicht weglassen. 😉

    Also de jure/de facto sind es zu viele Möglichkeiten, um das Programm nur durch Ausprobieren ausführbar zu machen.

    Ich werde in den von Euch genannten Quellen nach Online-Literatur suchen



  • regInfo000? schrieb:

    Also de jure/de facto sind es zu viele Möglichkeiten, um das Programm nur durch Ausprobieren ausführbar zu machen.

    Gerade in C heißt "ausführbar" noch lange nicht "läuft richtig".
    Und man muss wissen was man programmiert. Mit Ausprobieren kommst du da nicht weiter.



  • DirkB schrieb:

    Gerade in C heißt "ausführbar" noch lange nicht "läuft richtig".
    Und man muss wissen was man programmiert. Mit Ausprobieren kommst du da nicht weiter.

    Am Anfang ist "ausführbar" schon ein Erfolg.

    So, ich habe das jetzt erst mal so:

    /*
     * Kommentar
     */
    
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef unsigned char char255; /* 0..255 */
    
    int main(void) {
    	int a, b;
    	char c;
    
    	printf("!!Hello World!!\n");
    
    	printf("Bitte a und b (Ganzzahl) eingeben: ");
    	scanf("%i %i", &a, &b);
    	printf("a=%i, b=%i\n", a, b);
    
    	printf("a + b = %i\n",       a + b);
    	printf("a - b = %i\n",       a - b);
    	printf("a * b = %i\n",       a * b);
    	printf("a / (b | 1) = %i\n", a / (b | 1));
    	printf("a % (b | 1) = %i\n", a % (b | 1));
    	printf("b << 1 = %i\n",      b << 1);
    	printf("b >> 1 = %i\n",      b >> 1);
    
    	scanf("%c%c", &c, &c);
    	return EXIT_SUCCESS;
    }
    

    Facts: <limits.h> habe ich gar nicht benötigt. | hat keinen größeren Operatorrang (Präzedenz) als / und %. / 0 ist undefiniert (weder implementierungsabhängig noch unspezifiziert) und sollte immer vermieden werden.

    Dazu ein paar konkrete (<-- kein Hyperlink 😉 ) Fragen:

    scanf mit neuer Zeile scanf("%i %i\n" funktioniert bei mir nicht, generell verhält sich scanf komisch... - wieso, was kann man dagegen machen?

    getchar() (ohne actual parameters) führt bei mir nicht dazu, dass die Konsole nicht geschlossen wird, deshalb musste ich scanf("%c%c", &c, &c); (was ja unsinnig ist) schreiben... - wieso?

    Ist es besser EXIT_SUCCESS zu schreiben - oder einfach 0 (für "keine Fehler während der Programmlaufzeit")? Bei/Für EXIT_SUCCESS muss ich <stdlib.h> 'inkludieren', was die .exe hinterher etwas aufbläht.

    Wie funktioniert die Bedingte Anweisung ("? :" ??), kann ich damit erreichen, dass nicht durch 0 geteilt wird?

    Danke noch mal, ich bin geraden in dem C-Programmierung - Wikibooks bei 2.6 Fließkommazahlen (also noch nicht sehr weit). Wirklich super geschrieben, Sprachlich, Beispiele etc



  • regInfo000? schrieb:

    scanf mit neuer Zeile scanf("%i %i\n" funktioniert bei mir nicht, generell verhält sich scanf komisch... - wieso, was kann man dagegen machen?

    scanf ist ja auch zum Lesen gedacht, nicht zum Ausgeben.

    getchar() (ohne actual parameters) führt bei mir nicht dazu, dass die Konsole nicht geschlossen wird, deshalb musste ich scanf("%c%c", &c, &c); (was ja unsinnig ist) schreiben... - wieso?

    Egal, was ich hier jetzt schreiben werde: Gleich gibts die obligatorische SeppJ vs Unreg Diskussion über den C Konsolenpuffer. 🕶
    Jedenfalls ist das Enter von der letzten Eingabe noch im Puffer, was dazu führt, dass getchar() das liest. Um das zu vermeiden, muss man es aus dem Puffer löschen oder deine Variante verwenden.
    (alternativ auch zweimal getchar()).

    Ist es besser EXIT_SUCCESS zu schreiben - oder einfach 0 (für "keine Fehler während der Programmlaufzeit")? Bei/Für EXIT_SUCCESS muss ich <stdlib.h> 'inkludieren', was die .exe hinterher etwas aufbläht.

    EXIT_SUCCESS ist auf den meisten Plattformen 0, also kannst du das auch direkt schreiben.
    Aber das Inkludieren eines Headers erzeugt in der Regel keine Auswirkungen der exe size.

    Wie funktioniert die Bedingte Anweisung ("? :" ??), kann ich damit erreichen, dass nicht durch 0 geteilt wird?

    Die bedingte Bedingung ist wie if, nur in eine Zeile:

    if (b)
       result = a / b;
    else
       result = some_error_val;
    

    ->

    result = b ? a / b : some_error_val;
    

    Sinn des Beispiels mal außen vor gestellt.



  • Ein

    scanf(" %c", &c); // Achte auf das Leerzeichen vor dem &
    

    geht auch.
    scanf ist eine sehr komplexe Funktion. Man muss die Referenz dazu schon sehr genau lesen, damit man weiß wie was wo funktioniert.

    Zudem startet man Konsolenprogramme auch in der offenen Konsole.
    Viele Entwicklungsumgebungen bieten aber auch an, dass die Konsole offen bleibt, wenn man das Programm in der IDE startet.

    Das einbinden einer Headerdatei sollte keinen Einfluss auf die Dateigröße haben, da dort kein ausführbarer Code drin steht (stehen sollte).
    Um wieviel Byte wird die exe größer, wenn du stdlib.h einbindest.



  • Was haltet ihr von folgender Funktion, zwei int s miteinander zu vertauschen?:

    int swap(int a, int b);
    
    int main(void) {
    	/*....*/
    	printf("swap(a, b) = %i %i\n", a=swap(a,b), b=swap(a,b));
    
    	getchar();
    	getchar();
    	return EXIT_SUCCESS;
    }
    
    int swap(int a, int b) {
    	static int i = -1;
    	static int c = a;
    	static int d = b;
    	i++;
    	return i % 2 == 0 ? c : d;
    }
    

    Leider funktioniert's nicht ganz, weil er promptisch meldet, c und d dürfen bei der Initialisierung/Definition/Deklaration nur wirkliche Konstanten/Literale zugewiesen werden.

    @Nathan (der Weise? 😉 ): 2x getchar() gefällt mir besser und wie ich den Puffer leer bekomme, weiß ich nicht. Das gibt es Problem in anderen Sprachen auch.



  • Das wird nichts, da der Aufruf der Funktionen (bei printf) innerhalb von einem Sequenzpoint liegen.

    Du weißt nicht, in welcher Reihenfolge und mit welchen Werten die Funktion swap aufgerufen wird. (hat a beim aufruf von b=... schon den Wert von a ?)

    Was erwartest du von der Zuweisung static int c = a; ?
    Die Zuweisung vom Wert von a bei jedem Aufruf? Dann brauchst du kein static.
    Die Zuweisung vom Wert von a beim ersten Aufruf? Dann kannst du die Funktion für nichts anderes mehr benutzen.



  • DirkB schrieb:

    Zudem startet man Konsolenprogramme auch in der offenen Konsole.

    Danke! Ich dachte schon, ich wäre der einzige, der das hier bei diesen Gelegenheiten immer rausschreit ...



  • .exe-Dateien sind natürlich dafür da, durch einen Doppelklick vom Anwender geöffnet zu werden, und das ohne vorherige Installation.

    Ihr scheint das Wort "einfach" noch nicht zu verstehen.



  • Äh, nein.
    Das mag für Programme für eine GUI gelten.

    Und es muss noch nicht einmal eine .exe sein.

    Wenn du ein Konsolenprogramm schreibst, benutze es in der Konsole.
    Sonst schreib eins für die GUI. Aber das ist dann nicht mehr so einfach (sonst würdest du es schon machen)



  • regInfo000? schrieb:

    .exe-Dateien sind natürlich dafür da, durch einen Doppelklick vom Anwender geöffnet zu werden, und das ohne vorherige Installation.

    Ihr scheint das Wort "einfach" noch nicht zu verstehen.

    Wie DirkB schon beschrieben hat, kommt das sehr auf das Programm an. Wenn es nur durchläuft, eine Ausgabe produziert und dann terminiert, wirst Du die Ausgabe nie zu sehen bekommen.
    Aber wenn Du schon alles so gut weißt, wie das hier klingt ... wieso stellst Du dann überhaupt noch irgendwelche Fragen?



  • So, ich weiß selbstverständlich, wie man die Konsole benutzt, denn ich kompiliere ja C-Programmtext darüber.

    Später ist es nervig, ein Programm immer über cmd.exe aufrufen zu müssen, bzw., wenn es nach der Eingabe sofort wieder schließt.

    Jetzt habe ich ein paar weitere Fragen und so etwas hier geschrieben:

    /*
     * Kommentar
     */
    
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef unsigned char char255; /* 0..255 */
    
    int swap(int *hahaha, int *b);
    
    int main(void) {
    	int a, b;
    	printf("!!Hello World!!\n");
    	printf("Bitte a und b (Ganzzahl) eingeben: ");
    	scanf("%i %i", &a, &b);
    
    	printf("a=%i, b=%i\n", a, b);
    
    	printf("a + b = %i\n",       a + b);
    	printf("a - b = %i\n",       a - b);
    	printf("a * b = %i\n",       a * b);
    	printf("a / (b | 1) = %i\n", a / (b | 1));
    	printf("a % (b | 1) = %i\n", a % (b | 1));
    	printf("b << 1 = %i\n",      b << 1);
    	printf("b >> 1 = %i\n",      b >> 1);
    
    	swap(&a, &b);
    	printf("a=%i, b=%i\n", a, b);
    
    	printf("a + b = %i\n",       a + b);
    	printf("a - b = %i\n",       a - b);
    	printf("a * b = %i\n",       a * b);
    	printf("a / (b | 1) = %i\n", a / (b | 1));
    	printf("a % (b | 1) = %i\n", a % (b | 1));
    	printf("b << 1 = %i\n",      b << 1);
    	printf("b >> 1 = %i\n",      b >> 1);
    
    	printf("b | 1 << sizeof(1) * 8 - 1 = %i\n", b | 1 << sizeof(1) * 8 - 1);
    	getchar();
    	getchar();
    	return EXIT_SUCCESS;
    }
    
    int swap(int *a, int *b) {
    	*a ^= *b ^= *a;
    	return 1;
    }
    
    C:\Users\MyName\Desktop>%gcc% HelloWorld.c
    HelloWorld.c: In function 'main':
    HelloWorld.c:40:2: warning: suggest parentheses around '-' inside '<<' [-Wparent
    heses]
      printf("b | 1 << sizeof(1) * 8 - 1 = %i\n", b | 1 << sizeof(1) * 8 - 1);
      ^
    
    C:\Users\MyName\Desktop>HelloWorld.exe
    !!Hello World!!
    Bitte a und b (Ganzzahl) eingeben: 123 234
    a=123, b=234
    a + b = 357
    a - b = -111
    a * b = 28782
    a / (b | 1) = 0
    a % (b | 1) = 123
    b << 1 = 468
    b >> 1 = 117
    a=234, b=145
    a + b = 379
    a - b = 89
    a * b = 33930
    a / (b | 1) = 1
    a % (b | 1) = 89
    b << 1 = 290
    b >> 1 = 72
    b | 1 << sizeof(1) * 8 - 1 = -2147483503
    
    C:\Users\MyName\Desktop>
    

    Er warnt, in Zeile 40 zu wenige runde Klammern gesetzt zu haben. Die Rangfolge der Operatoren habe ich einfach mal hierher entnommen: http://www.imb-jena.de/~gmueller/kurse/c_c++/c_operat.html , dort steht, dass der nicht geklammerte Ausdruck meiner Intention entspricht.

    1. Wie sollte geklammert werden?

    2. sizeof(1) , sizeof(b) oder sizeof(int) verwenden? (1 | 1 << sizeof(1) * 8 - 1 entspricht bei mir -(2^31)+1, das ist also korrekt.)

    3. int swap(int *hahaha, int *b); funktioniert nicht richtig. Ich habe mir nur a b a gemerkt, was ist an dem swap abgesehen des returns falsch?

    4. Kennt ihr eine Seite, auf der "typedef struct" gut erklärt wird?

    ( 5) Kennt jemand eine [url=Nichtproportionale Schriftarten (Dickten-/Festbreitschrift)]http://de.wikipedia.org/wiki/Liste_von_Schriftarten#Nichtproportionale_Schriftarten_.28Dickten-.2FFestbreitschrift.29[/url], die nicht so viel Platz wie "Courier New" benötigt? )



  • Wenn du nu nicht den Kernighan willst:
    http://openbook.galileocomputing.de/c_von_a_bis_z/

    Setze Klammern immer, auch wenn die Assoziativität deiner "Intention" entspricht. Ist besser lesbar und eindeutig. Wie das geht, weißt du doch sicher noch aus dem Mathe-Unterricht aus der 6. Klasse?
    (Ich hoffe ja, du willst nicht beruflich programmieren...)


  • Mod

    regInfo000? schrieb:

    int swap(int *a, int *b) {
    	*a ^= *b ^= *a;
    	return 1;
    }
    
    1. int swap(int *hahaha, int *b); funktioniert nicht richtig. Ich habe mir nur a b a gemerkt, was ist an dem swap abgesehen des returns falsch?

    Es fehlt noch eine weitere Zuweisung an *b.
    Abgesehen führt diese Schreibweise zu undefiniertem Verhalten (es sei denn, es wird ein C++11-Compiler eingesetzt).
    Dieses swap ist destruktiv, wenn ein Objekt mit sich selbst geswapped wird, ausserdem ist es (mit modernen Prozessorarchitekturen, es sei denn der Compiler ist sehr schlau) weniger effizient als normales 3-Wege-swap.

    Korrekt wäre

    int swap(int *a, int *b) {
            *b ^= *a;
            *a ^= *b;
            *b ^= *a;
    	return 1;
    }
    


  • fibeline schrieb:

    (Ich hoffe ja, du willst nicht beruflich programmieren...)

    Doch, das möchte ich später mal machen.

    Ich hab' jetzt mal eine Personalverwaltung wie folgt geschrieben:

    /*
     * Kommentar
     */
    
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    enum Bereich { Management=30, Entwicklung=10, Produktion=1, Marketing=2, Vertrieb=1 };
    
    typedef struct {
    	char *name;
    	char *vorname;
    	unsigned int personalNummer;
    	enum Bereich abteilung;
    	float gehalt;
    	} Angestellter;
    
    Angestellter angestellte[10] = {
    	{ "aab", "aaa", 111, Vertrieb, 1111 },
    	{ "bbc", "bbb", 222, Marketing, 2222 },
    	{ "ccd", "ccc", 333, Produktion, 3333 },
    	{ "dde", "ddd", 444, Entwicklung, 4444 },
    	{ "Pan", "Peter", 555, Management, 9999 }
    };
    
    double durchschnitt(void) {
    	int i = 0;
    	double d = 0.0;
    	for (; i < 10; i++) {
    		d += angestellte[i].gehalt;
    	}
    	return d / 10.0;
    }
    
    void erhoeheGehalt(void) {
    	int i = 0;
    	for (; i < 10; i++) {
    		Angestellter *a = &angestellte[i];
    		a->gehalt *= a->abteilung / 100.0 + 1.0;
    	}
    }
    
    void drucke(Angestellter *ang) {
    	printf("%-10s%-10s%10i%10d%15f\n", (*ang).vorname, (*ang).name, (*ang).personalNummer, (*ang).abteilung, (*ang).gehalt);
    }
    
    void druckeAlle(void) {
    	int i = 0;
    	for (; i < 10; i++) {
    		drucke(&angestellte[i]);
    	}
    }
    
    int main(void) {
    	/* int a, b; */
    	druckeAlle();
    
    	printf("durchschnitt() = %f\n", durchschnitt());
    	erhoeheGehalt();
    	printf("durchschnitt() = %f\n", durchschnitt());
    
    	druckeAlle();
    	getchar();
    	return EXIT_SUCCESS;
    }
    

    Wie schreibe ich das ganze Ding jetzt so um, das ich 10 Array-Elemente oder ein dynamisch/es (wachsendes) Array/Feld benutzen kann?

    1. Wie scanf richtig benutzen?

    2. 4444.0F * (10 / 100.0 + 1.0) sollte 4888,4 ergeben, ergibt bei mir aber 4888,399902. Wie runde ich oder rechne genauer?

    3. Wie bekomme ich so eine schöne enum-Ausgabe hin, d. h., das da steht "Marketing" usw. usf. etc. pp.?

    Danke für eure Mitarbeit



  • regInfo000? schrieb:

    Wie schreibe ich das ganze Ding jetzt so um, das ich 10 Array-Elemente

    Sie erstmal zu, dass du die 5 vorhandenen richtig benutzt.
    Im Augenblick rechnest du mit Phantomdaten aus den Inizes 5 bis 9

    regInfo000? schrieb:

    oder ein dynamisch/es (wachsendes) Array/Feld benutzen kann?

    mit Zeigern und malloc

    regInfo000? schrieb:

    1. Wie scanf richtig benutzen?

    Referenz lesen und bei Problemen fragen.

    regInfo000? schrieb:

    1. 4444.0F * (10 / 100.0 + 1.0) sollte 4888,4 ergeben, ergibt bei mir aber 4888,399902. Wie runde ich oder rechne genauer?

    double nehmen. Bei Geldbeträgen rechnest du besser mit Ganzzahlen in Cent.

    regInfo000? schrieb:

    1. Wie bekomme ich so eine schöne enum-Ausgabe hin, d. h., das da steht "Marketing" usw. usf. etc. pp.?

    Selber machen. (mit Array oder switch/case ...)



  • Gut, danke erst mal.

    @DirkB: Warum meldest du dich denn hier nicht an (Registrierung), dann hast du doch sicherlich einige Vorteile, oder wurdest du in der vergangenheit mal "durch unfreundliches Verhalten vergrault/vertreiben"?

    Um auf dieser Ebene zu bleiben, und weil ich das andere Thema bzgl. Strings gelesen habe,

    wie kann ich ein/e String-Konstante/Literal verändern, oder wie lege ich veränderbare Zeichenketten in C an? Wie umgehe ich, dass String-Literale nicht während der gesamten Programmlaufzeit auf dem Stack, virtuellen Speicher, RAM/Festplatte o. Ä. liegen?

    Danke auch an dieser Stelle


Anmelden zum Antworten