Array von Strukturen



  • Deine Funktionen eingabe() und ausgabe() erwarten jeweils einen Parameter 'anz', du rufst sie aber ohne Parameter auf, das kann nur in die Hose gehen.
    (und der Prototyp "int eingabe();" sagt einem C-Compiler gar nichts, unter C++ hättest du hier vermutlich einen Linker-Fehler)

    PS: Übrigens gibst du in der ausgabe()-Funktion n-mal den ersten Eintrag des Arrays aus.



  • Danke! Jetzt muss ich versuchen es zu korrigieren, wenn es denn möglich ist. Ich nerv´ dich wahrscheinlich schon mit meinen Fragen, aber ich geb mir Mühe - auch wenn es vielleicht nicht so aussieht. 😃 Ich bin nicht faul, aber manchmal hänge ich fest und weiß nicht wonach genau ich suchen muss.



  • Ein paar Sachen die mir noch zum Ergänzen einfallen :

    1. Wenn ich mich nicht irre sollte in C eine Funktion, die keine Argumente erwartet so aussehen : void foo(void), also mit dem void-keyword in den Parameter-klammern. Ansonsten kann die Funktion eine unbestimmte Anzahl von Argumenten von unbestimmten Typ annehmen.
    2. Deine Ausgabe ist mir immernoch suspekt. Schauen wir sie uns mal an :

    int eingabe(int anz){
    
         int i;
    
         printf("Wieviele Konten sollen erstellt werden? ");  scanf("%i", &anz);
    
         for(i = 0; i<anz; ++i)
         {
               printf("Kontodaten fuer das %2i. Konto:\n", i+1);
               printf("Art des Kontos: ");    scanf("%2c", &konten[i].habenOderDarl);
               printf("Laufzeit: ");          scanf("%u", &konten[i].laufZt);
               printf("Guthaben/Darlehen: "); scanf("%d", &konten[i].kontoStnd);
         }
    
    }
    

    Naja, wie Cstoll bereits erwähnt hat macht natürliche der int-Parameter keinen Sinn. Aber : Du fragst den benutzer, wieviele Konten er erstellen will. Dann loops du dementsprechend oft. Aber du erstellst garnicht diese Anzahl an Konton, denn du hast diese per Konstante erstellt. Und zwar sind das 100. Gibt der benutzer nun 5000 als Anzahl ein, wirst du versuchen auf Speicher zuzugreifen, der dir garnicht gehört. Das wird bei konten[100] zu einer Speicherzugriffs-Verletzung führen und dein Programm wird abstürzen.
    Was du suchst ist wohl malloc(). Damit kannst du dynamisch Speicher anfordern - d.h die Grösse eines z.b Arrays muss NICHT zu Compilezeit festehen. Gcc unterstützt glaube ich sowas wie VLA ( Variable Length Arrray, oder wie es hieß ) aber normalerweise kommst du um das malloc nicht rum ( oder realloc, kenne C nicht so gut ).
    Wie du malloc benutzt lass dir bitte von CStoll zeigen, vllt. erbarmt er sich, denn mein Akku ist fast leer. Oder du googelst einfach nach "C malloc()" da findest du sicher was.

    Mit den Zinssatzkram da will ich mich jetzt nicht rumschlagen, das wirst du schon richtig gemacht haben 😛

    Mfg



  • Wow, danke für die umfangreiche Antwort, cvcv! Ich habs leider vermutet, dass ich ohne malloc nicht auskommen werde. Ich habe darüber in meinem C-Buch gelesen, aber leider nicht verstanden wie genau es angewendet wird. So wie ich es verstanden habe gehört es dann in meine eingabe()-Funktion. Dann ergibt sich für mich ein neues Problem, denn ich weiß nicht wie ich die Felder davon an die ausgabe()-Funktion übergeben soll. Ich vermute mal mit Zeigern... 😕 Puhhh... die erste Aufgabe, die wir programmieren sollten, war um einiges einfacher.

    Aber danke, dass ihr mir helft! Ohne euch wäre ich wohl schon längst verzweifelt...



  • Punkt 1: Bei der malloc()-Funktion gibst du an, wieviel Speicher (in Byte) du haben möchstest.
    Punkt 2: Um Werte von einer Funktion zur nächsten übergeben zu können, nutzt man normalerweise Parameter und Rückgabewerte:

    int eingabe(Konto** pkonten)
    {
      Konto* konten = NULL;
      int anz;
      printf("Wieviele Konten sollen erstellt werden? ");  scanf("%i", &anz);
      konten = malloc(anz * sizeof(Konto));//sizeof() gibt die Größe eines Datensatzes an
    
      for(i = 0; i<anz; ++i)
      {
        printf("Kontodaten fuer das %2i. Konto:\n", i+1);
        printf("Art des Kontos: ");    scanf("%2c", &konten[i].habenOderDarl);
        printf("Laufzeit: ");          scanf("%u", &konten[i].laufZt);
        printf("Guthaben/Darlehen: "); scanf("%d", &konten[i].kontoStnd);
      }
      *pkonten = konten;
      return;
    }
    
    void ausgabe(Konto* konten, int anzahl);
    {
      ...
    }
    
    //Anwendung:
    Konto* konten;
    int anz = eingabe(&konten);
    ausgabe(konten,anz);
    
    free(konten);//den per malloc() angeforderten Speicher wieder freigeben
    

    @cvcv: Herzlichen Dank für das Vertrauen 🙄



  • WHUT, ich habe ne Steckdose gefunden 😮

    So, ich hab mir malloc() mal eben reingezogen :

    http://www.cplusplus.com/ schrieb:

    void * malloc ( size_t size );

    Allocate memory block
    Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.

    The content of the newly allocated block of memory is not initialized, remaining with indeterminate values.

    Parameters

    size
    Size of the memory block, in bytes.

    So mal sehen ob ich das eben auf dein Programm übertragen kann ... müsste ja so dann aussehen :

    //Hier liest du wie gewohnt ein, wieviele Konten der Benutzer erstellen will
    Konto* konten = (Konto*)malloc( anz*sizeof(Konto) );
    //Hier loopst du dann.
    

    Du benutzt diesen "konten"-Zeiger jetzt einfach wie ein Array. Zb : konten[0].daten = bla; konten[1].daten = bub; etc.



  • @Cstoll Gerne gerne 🙂

    Ach : Gewöhn ihm doch gleich an schööön sauber zu sein, also prüf ob malloc() gefailed hat oder nicht 😛



  • Wow, mir fehlen die Worte. Danke, dass ihr euch die Zeit genommen habt! Echt super, dass es so nette Menschen wie euch gibt, die Newbies wie mir helfen. ^^

    Das muss ich mir jetzt ganz genau anschauen und auf mich wirken lassen. Ich hoffe, dass ich es diesesmal endlich hinkriege, denn es ist mir langsam echt schon peinlich, dass ich mit diesem Programm nicht klar komme.



  • cvcv schrieb:

    Ach : Gewöhn ihm doch gleich an schööön sauber zu sein, also prüf ob malloc() gefailed hat oder nicht 😛

    Kannst du ja machen, ich bin hier schließlich nicht der Allein-Unterhalter :p



  • @CStoll: Danke nochmal für deine Hilfe Gestern! Ich habe mir Gestern deinen Code mit malloc angeschaut und heute früh im Internet versucht klar zu machen wie genau malloc funktioniert.

    Folgende Fragen haben sich bei mir ergeben:

    Konto* konten = NULL;
    

    1. Diese Zeile ist mir nicht ganz klar. Wird NULL nicht zurückgegeben, wenn kein Speicher entsprechender Größe gefunden werden konnte? Oder stellt es in diesem Fall oben drüber, dass dem Zeiger *konten noch kein Speicher zugewiesen wurde?

    int anz = eingabe(&konten);
    

    2. Wenn ich diese Zeile oben in main() einbaue, dann kommt die Frage nach der Anzahl der Konten vor dem eigentlichen switch-menu. Oder mach ich etwas falsch?

    3. Wo genau gehört den free(konten); hin?



  • 1: Ist eine Gewohnheitssache - Variablen ohne eine Initialisierung haben einen undefinierten Inhalten, darum sollte man immer sofort einen Wert angeben. Und NULL steht einfach für "hier ist nichts".

    2: Die Abfrage der Anzahl erfolgt innerhalb der eingabe()-Funktion, die kannst du natürlich auch innerhalb deines switch()-Menüs einbauen. Allerdings solltest du in dem Fall die Funktion noch etwas anpassen und realloc() verwenden, sonst bekommst du ein Speicherleck, wenn der Nutzer mehrmals die Eingabe auswählt.

    3: das free() gehört ganz ans Ende des Programms, nachdem der Nutzer im Menü "Beenden" ausgewählt hat.



  • Zu 1: Ist jetzt nach deiner Antwort einleuchtend. Hab es erst so vermutet, aber wollte dennoch fragen.

    Zu 2: Ich denke auf realloc() verzichten, da unser Professor das jetzt nicht so genau nehmen wird. realloc() ist sicher noch ein Ticken komplizierter als malloc(). Da muss ich mich in den nächsten Wochen selber einarbeiten, wenn ich die Zeit dazu finde.

    Zu 3: Hab ich mir so gedacht und hab jetzt die Gewissheit. 🙂

    Ich kann dir garnicht genug danken für all die Hinweise und die Hilfe von dir.



  • realloc() ist eigentlich fast unverzichtbar, wenn du den reservierten Bereich nachträglich vergrößern willst. Die Alternative wäre nur, die bisherigen Datensätze manuell umzukopieren (malloc(),memcpy,free()).
    Andernfalls bekommst du ein technisches (Speicherleck) und logisches Problem, wenn du mehrmals die Eingabe-Funktion aufrufst:
    - Eingabe 4 Datensätze
    - Eingabe 5 Datensätze
    - Ausgabe -> nur die letzten 5 Datensätze werden ausgegeben

    Und so schwierig ist es in der Verwendung auch nicht 😉

    int eingabe(Konto** pkonten, int oldsize)
    {
      Konto* konten = NULL;
      int anz;
      printf("Wieviele Konten sollen erstellt werden? ");  scanf("%i", &anz);
      konten = realloc(*pkonten, (oldsize+anz) * sizeof(Konto));
    
      for(i = oldsize; i<oldsize+anz; ++i)
      {
        ...
      }
      *pkonten = konten;
      return oldsize+anz;
    }
    

    (für so eine Konstruktion ist es dann wichtig, daß der Pointer im Hauptprogramm mit NULL initialisiert wird, bevor er das erste Mal an eingabe() übergeben wird)



  • int eingabe(Konto** pkonten, int oldsize)
    {
      Konto* konten = NULL;
      int anz;
      printf("Wieviele Konten sollen erstellt werden? ");  scanf("%i", &anz);
      konten = realloc(*pkonten, (oldsize+anz) * sizeof(Konto));
      if ( konten == NULL ) {
         printf("Fehler : Es konnten keine (weiteren) Konten dieser Menge angelegt werden !\n");
         return oldsize;
      }
    
      for(i = oldsize; i<oldsize+anz; ++i)
      {
        ...
      }
      *pkonten = konten;
      return oldsize+anz;
    }
    

    😛



  • Ihr seid echt super! Danke euch vielmals! Eigentlich wollte ich es bei malloc() belassen, aber es freut mich, dass ihr mir an dem Programm realloc leicht verständlich gemacht habt. Ich hätte gedacht, dass es um einiges schwieriger sein wird, aber wenn ihr es erklärt, dann kapier ich es immer ziemlich schnell.



  • Nach langen Qualen bin ich tatsächlich noch fertig geworden - vor allem Dank euch natürlich. 🙂 Vermutlich hat das Programm noch ein paar Schwächen, aber Eingabe, Ausgaben bzw. Berechnungen stimmen jetzt.

    Falls jemand Zeit und Lust hat, dann kann er ja mal schnell drüber fliegen und Hinweise zur Verbesserungen geben. Für Kritik bin ich immer offen. 😉

    Hier der komplette Code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define DARLEHENSZINS 0.05
    #define GUTHABENSZINS 0.03
    #define KONTOANZ 100
    #define BANKNR
    
    typedef struct {
        unsigned int   kontoNr;              /* Kontonummer */
        double         kontoStnd;            /* Kontostand */
        double         zinsS;                /* Zinssatz */
        double         annuitaet;            /* Annuitaet */
        char           habenOderDarl;        /* Guthaben/Darlehen */
        unsigned int   laufZt;               /* Laufzeit */ 
    } Konto;
    
    int anz;
    
    int eingabe();                             /* Methode um Array mit Konten zu 
                                                fuellen */
    void ausgabe();                            /* Methode um Konto im Array wieder 
                                                auszugeben */                                               
    
    double kapZinsS(double kontoStnd, unsigned int laufZt);
    
    double darZinsS(double kontoStnd, double annuitaet);
    
    int main(int argc, char *argv[])
    {
        Konto* konten = NULL;
        int auswahl;                            /* Speicherung f¸r die Bedingung */
    
        printf("\n\t Zweites Programm in SS11, Strukturen");
        printf(" von xx");
        printf("\n\t uebersetzt am %s", __DATE__);
        printf("\n\t um %s Uhr", __TIME__);
        printf("\n\n");
    
        do {
            printf("-1- Neues Konto anlegen\n");
            printf("-2- Alle Kontodaten ausgeben\n");
            printf("-3- Programm beenden\n");
            printf("\nIhre Auswahl : ");
            scanf("%d", &auswahl);
    
            getchar();
            switch(auswahl) {
                case 1 : eingabe(&konten);
                    break;
                case 2 : ausgabe(konten,anz);
                    break;
                case 3 : printf("Programm beendet\n");
                    break;
                default: printf("Falsche Eingabe\n");
            }
        }while(auswahl <3);
    
        system("PAUSE"); 
        return 0;
    
    }
    
    int eingabe(Konto** pkonten)
    {
        int i;
        int oldsize = 0;
        Konto* konten = NULL;
    
        printf("Wieviele Konten sollen erstellt werden? ");  scanf("%i", &anz);
        konten = realloc(*pkonten, (oldsize+anz) * sizeof(Konto));
    
        for(i = oldsize; i<oldsize+anz; ++i) 
        {
            printf("Kontodaten fuer das %2i. Konto:\n", i+1);
            char art ;
            printf("Art des Kontos: ");    scanf(" %c", &art);
            konten[i].habenOderDarl = art;
            int kontostand;
            printf("G/D Betrag: ");        scanf("%i", &kontostand); 
            konten[i].kontoStnd = kontostand;
            if(art == 'g'){
            int laufzeit;
            printf("Laufzeit: ");          scanf("%u", &laufzeit);
            konten[i].laufZt = laufzeit;
            }
            if(art == 'd'){
            int ann;
            printf("Annuitaet: ");         scanf("%i", &ann);
            konten[i].annuitaet = ann;
            }
    
        }
        *pkonten = konten;
        return oldsize+anz;
    }
    
    void ausgabe(Konto* konten, int anz){
    
        int i;
    
        for(i = 0; i<anz; i++){
    
            if(konten[i].habenOderDarl == 'g')
            {
                kapZinsS(konten[i].kontoStnd, konten[i].laufZt);
                printf("\n");
            }
    
            if(konten[i].habenOderDarl == 'd')
            { 
                darZinsS(konten[i].kontoStnd, konten[i].annuitaet);
                printf("\n");
            }
        }                 
    }
    
    /* Berechnung der Kapitalanlage mit Schleife */
    double kapZinsS(double kontoStnd, unsigned int laufZt)
    {
        int i; 
    
        for(i=1; i<=laufZt; i++){
            double zins;       
            zins = kontoStnd * GUTHABENSZINS;      
            kontoStnd = kontoStnd + zins;
            printf("Jahr: %u\n", i);
            printf("Anlaufende Zinsen: %.2lf \n", zins); 
            printf("Kapital nach Zinsen: %.2lf \n", kontoStnd);         
        }
        return kontoStnd;
    }
    
    /* Berechnung der Tilgung mit Schleife */
    double darZinsS(double kontoStnd, double annuitaet)
    {
        int i;
        double zins;
        double tilgungsrate;
    
        for(i=1; i <= kontoStnd; i++){
    
            zins = kontoStnd * DARLEHENSZINS;
            tilgungsrate = annuitaet - zins;
            kontoStnd = kontoStnd - tilgungsrate;
            printf("Jahr: %u\n", i);
            printf("Zinsen: %lf \n", zins);   
            printf("Restschuld: %.lf \n", kontoStnd); 
        }
        return kontoStnd;
    }
    

    @CStoll: Falls du es sehen solltest, kannst du mir eventuell einen Tipp geben, ob man die eingabe() ohne großen Aufwand korrigieren kann, damit es mit realloc() -> oldsize richtig funktioniert. Ich hab es leider nicht so hinbekommen wie du es mir gezeigt hast und es kamen immer ganz komische Zahlen bei den Berechnungen raus.

    Danke schonmal für alle eure Hinweise und Hilfe. Ohne euch hätte ich die Aufgabe vermutlich nicht hinbekommen. 🙂 👍



  • Du solltest die alte Größe des Arrays auch als Parameter mit übergeben, damit sie das Ende der Funktion überlebt:

    int eingabe(Konto** pkonten,int oldsize)
    {
      ...
    }
    
    //in der main:
    Konto* konten = NULL;
    int size = 0;
    ...
    switch(auswahl)
    {
    case 1:
      size = eingabe(&konten, size);
      break;
    case 2:
      ausgabe(konten, size);
      break;
    ...
    }
    

    PS: Die Prototypen der Funktionen eingabe() und ausgabe() sollten nach Möglichkeit zu den später folgenden Definitonen passen, das hilft dir und dem Compiler. Und die globale Variable int anz; bringt dir auch keine Pluspunkte.



  • Ahhhhhhh was habt ihr gegen meine Überprüfung, ob malloc/realloc gefailed hat oder nicht ? :p Besonders wenn man immer wieder speicher anfordert :o



  • Danke, CStoll! Jetzt funktioniert auch das, auch wenn nur mit der globalen Variable anz. Ich krieg es ohne die globale Variable irgendwie nicht hin, bin wohl zu blöd dazu. 😃

    Jetzt bin ich richtig happy weil alles funktioniert, ich alles halbwegs verstanden habe und morgen wohl mein Testat bekomme. 🙂

    cvcv schrieb:

    Ahhhhhhh was habt ihr gegen meine Überprüfung, ob malloc/realloc gefailed hat oder nicht ? :p Besonders wenn man immer wieder speicher anfordert :o

    Ach sorry, das baue ich natürlich auch noch rein. 😉 Hab nur rumexperementiert und fand es mit CStoll´s Version etwas übersichtlicher. Ich bin noch ein Newbie und große Programmiercodes machen mir Angst... 😃 😃



  • Die Parameter-Übergabe an die Funktion ausgabe() hast du doch auch hingekriegt, so ähnlich sollte es auch bei der eingabe() funktionieren. Du mußt nur daran denken, daß die neue Arraygröße als Rückgabewert herausgegeben wird - und diesen Rückgabewert entgegennehmen.

    cvcv schrieb:

    Ahhhhhhh was habt ihr gegen meine Überprüfung, ob malloc/realloc gefailed hat oder nicht ? :p Besonders wenn man immer wieder speicher anfordert :o

    Nimm's nicht persönlich - ich bin halt C++-verwöhnt 😃
    (weißt du eigentlich, ob nach einem misslungenen realloc()-Aufruf der bisherige Speicherbereich noch gültig ist?)

    @mr unknown: Die zwei Zeilen zur Auswertung des realloc()-Ergebnisses solltest du dir aber wirklich noch gönnen.


Anmelden zum Antworten