Einige Probleme mit einem Projekt



  • Erstmal Hallo!

    Ich muss ein "simples" Kontoprogramm schreiben, leider gibt es noch einige Probleme, mit denen ich nicht zurecht komme 😕

    Am meisten macht mir das Speichern der eingegeben Daten in ein stuct bzw. anschließend in eine Datei Probleme.

    Durch diverses Ausprobieren verschiedener Ansätze, ist im Code wohl einiges durcheinander gekommen...

    Wäre nett, wenn ihr mal drüberschauen könnt.

    konto.h

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <conio.h>
    
    const int LAENGE_NAME = 20;
    
    struct konto
    { 
        char vorname[LAENGE_NAME]; 
        char nachname[LAENGE_NAME]; 
        float kontostand; 
        unsigned int kontonummer; 
    };
    typedef struct konto konto;
    
    void erstelle_konto(konto *, int); 
    void loesche_konto(konto *); 
    void zeige_konto(konto *); 
    void speichere_konto(konto *);
    void ueberweisung(konto *, float); 
    
    void zentrierteAusgabe(char text[], int zeilenumbruch, int verschiebung)
    {
    	for(int i=0;i<=int((80 - strlen(text))/2)-verschiebung;i++)
    	{
    		printf(" ");
    	}
    	printf("%s", text);
    
    	for(int i = 0; i < zeilenumbruch; i++)
    	{
    		printf("\n");
    	}
    }
    

    main.cpp

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include "konto.h" 
    #include <conio.h>
    int main() 
    { 
    	konto konten;
        int auswahl; 
    	int IDZaehler = 100; 
        int kontoID;
        int kontoID2; 
        float guthaben; 
    
    	while(1) 
        { 
    		zentrierteAusgabe( "---------------------------------KONTOFUEHRUNG--------------------------------",4,1);
    		printf("Herzlich Willkommen \n");
    		printf("------------------- \n\n");
            printf("1) Konto erstellen\n"); 
            printf("2) Konto anzeigen\n"); 
            printf("3) Konto loeschen\n"); 
            printf("4) Ueberweisung\n"); 
            printf("0) Beenden\n\n"); 
    		printf("------------------- \n");
            printf("Auswahl: "); 
    		scanf("%d", &auswahl); 
    
    		switch(auswahl) 
            { 
                case 0: 
    				speichere_konto(&konten);
                    return 0; 
                    break; 
                case 1: 
                    erstelle_konto(&konten, IDZaehler); 
                    IDZaehler++; 
                    break; 
                case 2: 
                    printf("Geben sie die Kontonummer ein: "); 
    				scanf("%d", &konten); 
                    zeige_konto(&konten); 
                    break; 
                case 3: 
                    printf("Geben sie die Kontonummer ein: "); 
    				scanf("%d", &konten); 
                    loesche_konto(&konten); 
    				printf("Konto wurde geloescht\n");
                    break; 
                case 4: 
                    printf("Geben sie die Kontonummer ein, von der abgebucht werden soll: "); 
    				scanf("%d", &konten); 
                    printf("Geben sie die Kontonummer ein, an die ueberwiesen werden soll: "); 
    				scanf("%d", &konten); 
                    printf("Geben sie den Betrag ein, der ueberwiesen werden soll: "); 
    				scanf("%f", &guthaben); 
                    ueberweisung(&konten, guthaben); 
                    break; 
                default: 
                    break; 
            } 
    		printf("Druecken Sie eine beliebige Taste um vorzufahren");
    		getch();
    		system("cls");
        } 
        return 0; 
    }
    
    void erstelle_konto(konto *konten, int) 
    { 
    	printf("\n\nVorname:     "); 
    	scanf("%s", &konten->vorname); 
        printf("Nachname:    "); 
    	scanf("%s", &konten->nachname); 
        printf("Kontostand:  "); 
    	scanf("%f", &konten->kontostand); 
        printf("Kontonummer: %d\n", konten->kontonummer); 
        printf("Konto wurde erstellt!\n\n\n"); 
    } 
    
    void loesche_konto(konto *konten) //man kann nichtvorhandene konten löschen
    { 
        strcpy(konten->vorname, "\0"); 
        strcpy(konten->nachname, "\0"); 
        konten->kontostand = 0; 
    } 
    
    void zeige_konto(konto *konten) 
    { 
        if(strlen(konten->vorname) == 0 || strlen(konten->nachname) == 0) 
    	{ 
            printf("\n\nDieses Konto existiert nicht!\n\n\n"); 
        } 
        else 
        { 
            printf("\n\nVorname:     %s\n", konten->vorname); 
            printf("Nachname:    %s\n", konten->nachname); 
            printf("Kontostand:  %.2f\n", konten->kontostand); 
            printf("Kontonummer: %d\n\n\n", konten->kontonummer); 
        } 
    } 
    
    void speichere_konto(konto *konten) 
    { 
    	char zeichen;
    	FILE *datei; 
        datei = fopen("Konten.txt", "a+"); 
    
    	if(datei == NULL)
    	{
    		printf("\nFEHLER BEIM SPEICHERN"); 
    	}
    
    	else
    	{
    		fprintf(datei, "Kontonummer: %d\nVorname:     %s\nNachname:    %s\nKontostand:  %.2f\n\n", konten->kontonummer, konten->vorname, konten->nachname, konten->kontostand); 
    	}
        fclose(datei); 
    } 
    
    void ueberweisung(konto *konten, float guthaben) 
    { 
        if(konten->kontostand - guthaben < 0) 
        { 
            printf("\n\nNicht genug Guthaben!\n\n"); 
        } 
        else 
        { 
            konten->kontostand = konten->kontostand - guthaben; 
            konten->kontostand = konten->kontostand + guthaben; 
            printf("\n\nUeberweisung erfolgreich!\n\n"); 
        } 
    }
    

    Schonmal vielen Dank im voraus!



  • Und was ist jetzt deine Frage?

    Du hast nur ein Konto, auch wenn du die Variable konten nennst.
    float ist ein schlechter Datentyp für Geldbeträge. double auch. Rechne in Cent.
    Eine Kontonummer ist keine Ganzzahl.

    Vergleich mal Zeile 46 mit Zeile 76. Du willst da dasgleiche einlesen.
    Ok, bei dem einen hast du einen Zeiger, bei dem anderen die Variable selber. Trotzdem fehlt was.
    Und nicht nur in Zeile 46.

    Ein Leerstring ist übrigens "". Der enthält schon '\0'.
    Alternative zu deinem strcpy wäre konten->vorname[0] = '\0';

    Variablen und Funktionsdefinitionen haben in Headerdateien (.h) nichts zu suchen. (zentrierteAusgabe)
    Achja, zentrierteAusgabe. Schau dir mal die Möglichleiten von printf an. Gerade die width und .precision in Zusammenspiel mit dem * bei %s bieten sehr viel Möglichkeiten.

    Bei einer Überweisung sind meist zwei Konten beteiligt.

    Du hast ein C-Programm, dann gib deiner Datei auch eine .c Erweiterung.

    Stelle den Warnlevel vom Compiler auf Maximum, beachte die Warnungen und behebe deren Ursache.

    Reicht das erstmal?


  • Mod

    const int LAENGE_NAME = 20;
    
    struct konto
    {
        char vorname[LAENGE_NAME];
        char nachname[LAENGE_NAME];
        float kontostand;
        unsigned int kontonummer;
    };
    typedef struct konto konto;
    

    Das geht ja schon einmal schlecht los. 20 Zeichen für Namen? Ein bisschen knapp, oder? float ist wohl der schlechteste Datentyp für diskrete Größen wie Geldbeträge. unsigned ist kein geeigneter Datentyp für Ziffernfolgen. Willst du mit den Kontonummern etwa rechnen? Wohl kaum.

    Wichtig: Das ist nicht einmal C! Das (genauer gesagt: Die Integerkonstante als Arraygröße) geht so nur in C++ (wobei das ganze Programm in "richtigem" C++ aber komplett anders aussehen würde). Du benutzt wohl einen C++-Compiler für dein C-Programm. Tu das nicht!

    void zeige_konto(konto *);
    void speichere_konto(konto *);
    

    Const-correctness beachten.

    void erstelle_konto(konto *konten, int)
    

    Wozu der int?
    Wieso ist erstelle_konto eine interaktive Fragerunde? Trenne Logik und Oberfläche! Das gilt für dein gesamtes Programm, da gibt es viele Stellen dieser Art.

    konten->kontostand = konten->kontostand - guthaben;
            konten->kontostand = konten->kontostand + guthaben;
            printf("\n\nUeberweisung erfolgreich!\n\n");
    

    Eine sehr sinnvolle Art der Überweisung [/Ironie]

    Am meisten macht mir das Speichern der eingegeben Daten in ein stuct bzw. anschließend in eine Datei Probleme.

    Da wiederum verstehe ich nicht, was deine Schwierigkeit ist. Kannst du etwas genauer sagen, was dein Problem ist?

    for(int i=0;i<=int((80 - strlen(text))/2)-verschiebung;i++)
    

    Noch etwas, das nur in C++ geht. In C gibt es keine Konstruktoren.

    Du hast dutzende unbenutzte Parameter und Variablen über das ganze Programm verteilt.

    Wie du scanf benutzt ist an vielen Stellen total falsch.

    void erstelle_konto(konto *konten, int)
    

    Noch etwas, das nur in C++ geht. In C darfst du die Namen von Parametern nicht weglassen.

    Das war, was mir und meinem Compiler beim ersten Durchlesen aufgefallen ist. Das ist eine ganze Menge. Das ist nicht gut 👎 .

    Erste Gegenmaßnahmen:
    - C Compiler benutzen. Viele Compiler können C und C++, du hast deinen wohl auf C++ eingestellt.
    - Viele der Fehler sind einfache Fehler, die der Compiler auch sieht, aber die technisch gesehen korrektes C sind (aber einfach keinen Sinn machen). Compiler können dazu Warnungen ausgeben. Mach dich schlau, wie das bei dir geht. Stell das Warnungslevel auf Maximum, beseitige alle Warnungen (und zwar die Ursache, nicht indem du den Compiler mit Casts oder ähnlichem ruhigstellst!)

    Diese beiden Punkte alleine werden dich eine Weile beschäftigen und du wirst eine Menge lernen müssen dabei, darüber, wie korrektes C überhaupt aussieht.



  • Danke für die Kritik.
    Aber wie man sieht, bin ich nicht wirklich ein Programmiergenie, ich hab mich durch diverse Skripte und Tutorials gekämpft, um überhaupt so weit zu kommen.

    Um es auf den Punkt zu bringen, ich kann mit den meisten Hilfestellungen leider nichts anfangen...

    das mit dem compiler ist so ne Sache, ich nutze Visual Studio 2010 Express, hat das überhaupt nen c-compiler?
    Ich hab die main.cpp in main.c geändert und die Warnmeldungen auf max gestellt, trotzdem zeigt er mir, bis auf Sicherheitshinweise, keine Fehler, oder Warnungen an.

    Ich installiere gerade die Ultimate Version, vllt funktionierts damit ja.



  • SeppJ schrieb:

    ...
    Wichtig: Das ist nicht einmal C! Das (genauer gesagt: Die Integerkonstante als Arraygröße) geht so nur in C++ (wobei das ganze Programm in "richtigem" C++ aber komplett anders aussehen würde). Du benutzt wohl einen C++-Compiler für dein C-Programm. Tu das nicht!
    ...

    Hi, kannst Du das genauer erläutern? Also geht es hier darum, dass die Arraygröße durch 'const int' angegeben wird? Würde mich wirklich interessieren.
    Auch warum es nur in C++ geht ... Also ginge denn:

    typedef struct bla {
      char blabla[128];
    } BLA;
    

    ? (Ich habe nämlich in einem Projekt ein Vorkommen dessen)


  • Mod

    phf schrieb:

    Also geht es hier darum, dass die Arraygröße durch 'const int' angegeben wird?

    Ja, genau.

    Würde mich wirklich interessieren.
    Auch warum es nur in C++ geht

    Ist nun einmal einfach so. In C geht es nicht. C++ kann in fast jeder Hinsicht mehr als C. So auch hier.

    ... Also ginge denn:

    typedef struct bla {
      char blabla[128];
    } BLA;
    

    ? (Ich habe nämlich in einem Projekt ein Vorkommen dessen)

    Ja, klar geht das.


  • Mod

    PepTic schrieb:

    das mit dem compiler ist so ne Sache, ich nutze Visual Studio 2010 Express, hat das überhaupt nen c-compiler?

    Ja, hat es. Wobei du mit dem noch viel mehr Inkompatiblitäten feststellen wirst, als ich genannt habe. C hat im Lauf der Jahre einige Features neu dazu bekommen. Die wichtigsten Meilensteine waren 1989, 1999 und 2011. Darunter auch einige C++-Features, die nach C zurück geflossen sind.
    Problem: Der Microsoft C-Compiler kann nur C wie aus dem Jahr 1989 (was aber durchaus nicht heißen soll, dass das schlecht ist). Du hast aber einige der C-Features aus den neueren Sprachversionen benutzt. Mit dem C++-Compiler sind dir diese ebenfalls nicht aufgefallen, da diese Features auch Teil von C++ sind.



  • Okay, vielen Dank.
    Ich frage mich, was der Unterschied zwischen einem const int, einem fest als Arraygröße eingetragenem Zahlenwert und auch z.B. einem 'define VALUE (128)'
    wäre. 😕

    Edit: Also char bla[VALUE];


  • Mod

    Eine const Variable ist in C wie eine normale Variable, bloß mit Compilerfehler, wenn man sie zu ändern versucht. Sie hat ansonsten für den Compiler keine Sonderbedeutung.

    Ein define ist nur eine Textersetzung, die stattfindet, bevor der Compiler überhaupt dran kommt.

    #define Bla Blupp
    Bla Bla
    

    Der Compiler sieht nur:

    Blupp Blupp
    

    Eine feste Zahl im Programm ist eine echte Konstante für den Compiler und kann daher als Arraygröße benutzt werden.



  • thx 🙂



  • Also jetzt bekomme ich einige Warnungen angezeigt.

    Der Rückgabewert wird ignoriert: "scanf" main.c Zeile 71
    Der 2-Parameter im Aufruf von "scanf" muss die Adresse der Zeichenfolge sein. main.c Zeile 71

    Ich weiß aber ehrlich gesagt nicht was ich da jetzt ändern muss.

    Auch mit den Hilfestellungen von euch kann ich nicht wirklich was anfangen, wie gesagt, hab den code mit mühe und not gerade so hinbekommen wie er ist...

    Könnt ihr vllt noch mehr ins Detail gehen?


  • Mod

    PepTic schrieb:

    Der Rückgabewert wird ignoriert: "scanf" main.c Zeile 71
    Der 2-Parameter im Aufruf von "scanf" muss die Adresse der Zeichenfolge sein. main.c Zeile 71

    Ein dicker Fehler, eine Designsschwäche:
    Fehler: Du willst eine Zeichenkette einlesen. Du gibst aber die Adresse von etwas anderem an. Ich weiß nicht genau, wie dein neuer Code aussieht, aber wahrscheinlich bezieht sich das auf eine Zeile wie Zeile 41 im Eröffnungsbeitrag, wo du die Adresse eines Kontos übergibst, statt die Adresse des gewünschten Attributs dieses Kontos.
    Designschwäche: Eingaben können auch fehlschlagen. Wenn du den Rückgabewert der Eingabefunktionen nicht prüfst, dann arbeitest du mit irgendwelchen Müllwerten weiter, ohne es zu merken.



  • danke für deine Hilfe, ich weiß aber wie gesagt nicht, wie ich es umsetzen soll.



  • Ich möchte ja keine Komplettlösung, nur ein paar Codeansätze würden mir sicher helfen.


  • Mod

    PepTic schrieb:

    Ich möchte ja keine Komplettlösung, nur ein paar Codeansätze würden mir sicher helfen.

    Mir ist nicht klar, womit du noch Schwierigkeiten hast.
    Wenn dir scanf oder einfache Kontrollflussverzweigungen Probleme machen, dann geh nochmal zurück zu den ersten Kapiteln deines Buches. Dies ist ja kein Problem, irgendwelche komplizierten Ausdrücke mit scanf-Formatstringmagie zu parsen, sondern bloß die absolute Basisfunktionalität, wie man einfache Integer oder Zeichenketten einliest. Das ist Grundwissen.



  • Hab den Code nochmal überarbeitet:

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include "konto.h" 
    #include <conio.h>
    int main() 
    { 
        int auswahl; 
    	int IDZaehler = 100; 
    	int kontoID;
    	int kontoID2;
        double guthaben; 
    
    	do
        { 
    		printf("-----------------------------------KONTOFUEHRUNG--------------------------------\n");
    		printf("Herzlich Willkommen \n");
    		printf("------------------- \n\n");
            printf("1) Konto erstellen\n"); 
            printf("2) Konto anzeigen\n"); 
            printf("3) Konto loeschen\n"); 
            printf("4) Ueberweisung\n"); 
            printf("0) Beenden\n\n"); 
    		printf("------------------- \n");
            printf("Auswahl: "); 
    		scanf("%i", &auswahl);
    
    		switch(auswahl) 
            { 
                case 0: 
    				speichere_konto(kontoID);
                    return 0; 
                    break; 
                case 1: 
                    erstelle_konto(IDZaehler); 
                    IDZaehler++; 
                    break; 
                case 2: 
                    printf("Geben sie die Kontonummer ein: "); 
    				scanf("%d", &kontoID); 
                    zeige_konto(kontoID); 
                    break; 
                case 3: 
                    printf("Geben sie die Kontonummer ein: "); 
    				scanf("%d", &kontoID); 
                    loesche_konto(kontoID); 
    				printf("Konto wurde geloescht\n");
                    break; 
                case 4: 
                    printf("Geben sie die Kontonummer ein, von der abgebucht werden soll: "); 
    				scanf("%d", &kontoID); 
                    printf("Geben sie die Kontonummer ein, an die ueberwiesen werden soll: "); 
    				scanf("%d", &kontoID2); 
                    printf("Geben sie den Betrag ein, der ueberwiesen werden soll: "); 
    				scanf("%f", &guthaben); 
                    ueberweisung(kontoID, kontoID2, guthaben); 
                    break; 
                default: 
                    break; 
            } 
    		printf("Druecken Sie eine beliebige Taste um vorzufahren");
    		getch();
    		system("cls");
        } while(auswahl);
    		return;
    }
    
    void erstelle_konto(int ID) 
    { 
    
    	printf("\n\nVorname:     "); 
    	scanf("%s", konten[ID].vorname); 
        printf("Nachname:    "); 
    	scanf("%s", konten[ID].nachname); 
        printf("Kontostand:  "); 
    	scanf("%f", &konten[ID].kontostand); 
        printf("Kontonummer: %d\n", ID); 
    	konten[ID].kontonummer = ID;
        printf("Konto wurde erstellt!\n\n\n"); 
    
    } 
    
    void loesche_konto(int ID) //man kann nichtvorhandene konten löschen
    { 
        strcpy(konten[ID].vorname, ""); 
        strcpy(konten[ID].nachname, ""); 
        konten[ID].kontostand = 0; 
    } 
    
    void zeige_konto(int ID) 
    { 
        if(strlen(konten->vorname) == 0 || strlen(konten->nachname) == 0) 
    	{ 
            printf("\n\nDieses Konto existiert nicht!\n\n\n"); 
        } 
        else 
        { 
            printf("\n\nVorname:     %s\n", konten[ID].vorname); 
            printf("Nachname:    %s\n", konten[ID].nachname); 
            printf("Kontostand:  %.2f\n", konten[ID].kontostand); 
            printf("Kontonummer: %d\n\n\n", konten[ID].kontonummer); 
        } 
    } 
    
    void speichere_konto(int ID) 
    { 
    
    	FILE *datei; 
        datei = fopen("Konten.txt", "a+"); 
    
    	if(datei == NULL)
    
    	{
    		printf("\nFEHLER BEIM SPEICHERN"); 
    		return;
    	}
    
    	else
    	{
    		fprintf(datei, "Kontonummer: %d\nVorname:     %s\nNachname:    %s\nKontostand:  %.2f\n\n", konten[ID].kontonummer, konten[ID].vorname, konten[ID].nachname, konten[ID].kontostand); 
    	}
        fclose(datei); 
    
    } 
    
    void ueberweisung(int vonID, int zuID, double guthaben) 
    { 
    
        if(konten->kontostand - guthaben < 0) 
        { 
            printf("\n\nNicht genug Guthaben!\n\n"); 
        } 
        else 
        { 
            konten[vonID].kontostand = konten[vonID].kontostand - guthaben; 
            konten[zuID].kontostand = konten[zuID].kontostand + guthaben; 
            printf("\n\nUeberweisung erfolgreich!\n\n"); 
        } 
    }
    
    struct konto
    { 
        char vorname[20]; 
        char nachname[20]; 
        double kontostand; 
        int kontonummer; 
    }konten[1000];
    typedef struct konto konto;
    
    void erstelle_konto(int); 
    void loesche_konto(int); 
    void zeige_konto(int); 
    void speichere_konto(int);
    void ueberweisung(int, int, double);
    

    Wenn ich jetzt Konto anzeigen auswähle, sagt er mir es existiert nicht.
    in die Datei wird es aber geschrieben, nur der Kontostand ist 0.00

    bekomme außerdem diese Compilerfehler:
    Nicht initialisierter Speicher "kontoID" wird verwendet.: Lines: 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, 31, 32

    und

    Die möglicherweise nicht initialisierte lokale Variable "kontoID" wurde verwendet.

    Das Problem mit dem Rückgabewert besteht immer noch bei scanf und getch.



  • PepTic schrieb:

    Nicht initialisierter Speicher "kontoID" wird verwendet.: Lines: 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, 31, 32

    Du hast der Variablen "kontoID" vor der Nutzung noch keinen Wert zugeteilt. Die Variable ist nicht initialisiert.
    Lokale Variablen werden nicht automatisch mit 0 Initiolisiert.

    Weißt du, was der Rückgabewert einer Funktion ist?

    Du hast jetzt ein globales Array mit deinen Kontendaten.
    Der Index in dem Array hat eigentlich wenig mit der Kontonummer zu tun.

    DirkB schrieb:

    float ist ein schlechter Datentyp für Geldbeträge. double auch. Rechne in Cent.
    Eine Kontonummer ist keine Ganzzahl.

    Jetzt hast du int für die Kontonummer. Da war unsigned ja noch richtig gut gegen.


  • Mod

    edit: Dein Compiler hat doch recht 🙂 . Hatte den case 0 nicht gesehen.

    Weitere Fehler:
    Hier dran ist falsch (nur Compilerdiagnostik, nicht das was ich sonst noch ändern würde):
    - %f ist nicht der scanf-Formatspezifizierer für double, sondern für float. scanf ist nicht das genaue Gegenstück zu printf!
    -Du hast in der main ein return ohne Wert, aber main gibt int zurück!



  • Du hast der Variablen "kontoID" vor der Nutzung noch keinen Wert zugeteilt. Die Variable ist nicht initialisiert.
    Lokale Variablen werden nicht automatisch mit 0 Initiolisiert.

    Weißt du, was der Rückgabewert einer Funktion ist?

    Hab nun ne den Wert auf 0 gesetzt, trotzdem kann ich durch Eingabe der Kontonummer nichts einsehen.

    - %f ist nicht der scanf-Formatspezifizierer für double, sondern für float. scanf ist nicht das genaue Gegenstück zu printf!

    läuft jetzt!

    Du hast jetzt ein globales Array mit deinen Kontendaten.
    Der Index in dem Array hat eigentlich wenig mit der Kontonummer zu tun.

    und wie müsste es aussehen?

    Jetzt hast du int für die Kontonummer. Da war unsigned ja noch richtig gut gegen.

    die Kontonummer besteht doch nur aus höchstens 4Ziffern, was empfiehlst du denn?

    edit:
    Jetzt passt soweit alles, nur möchte ich, dass ich zu Beginn des Programms auf den Inhalt der Datei zugreifen und ihn verwenden kann.


Anmelden zum Antworten