Array of structure



  • Wird die Größe nicht über Malloc bestimmt?

    Die Funktion ist jetzt nach der Struct deklariert.

    Die Funktion hat jetzt einen zweiten Parameter.

    Dass Scanf eine Adresse will, funktioniert doch mit '&', dachte ich? & gibt immer die Adresse aus, so war meine Annahme.. (oder &database[n].mark ?)

    Hier der aktuelle Stand:

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXMATRNUM   6  
    #define MAXEXAMIDNUM    6
    
    struct list
    {                                  
        char Matr[MAXMATRNUM+1];
        char ExamID[MAXEXAMIDNUM+1];
        float mark;
    };
    
    struct list *database;
    
    char passedTest(*database);
    
    int main()
    {
        int n,result,i;
        printf("Please enter the number of exams: ");
        scanf("%d", &n);
        database = malloc (n * sizeof(struct list));
        if (database == 0)
            return EXIT_FAILURE;
    
        for(i=0; i<n; i++)
        {
            printf("Please enter the Matriculation Number %d",i);
            scanf("%c",&*database[n].Matr);
            printf("Please enter the Exam-ID %d",i);
            scanf("%c",&*database[n].ExamID);
            printf("Please enter the Mark %d",i);
            scanf("%f",&*database[n].mark);
        }
    
        result=passedTest(*database);
    
        printf("The number of passed exams is: %d", result);
    
        free (database);
        return EXIT_SUCCESS;
    }
    
    char passedTest(*database)
    {
        int num=0,i,n;
    
        for(i=0;i<n;i++)
        {
        if(*database[n].mark<=4.0)
        num++;
        }
        return num;
    }
    


  • Tergo schrieb:

    Wird die Größe nicht über Malloc bestimmt?

    Du kannst die Größe aber anhand des Zeigers nicht mehr ermitteln. Darum musst du sie dir extra merken und immer mitführen.

    Tergo schrieb:

    Die Funktion ist jetzt nach der Struct deklariert.

    OK.
    Aber warum ist dataset jetzt global?

    Tergo schrieb:

    Die Funktion hat jetzt einen zweiten Parameter.

    Lüge.

    Tergo schrieb:

    Dass Scanf eine Adresse will, funktioniert doch mit '&', dachte ich? & gibt immer die Adresse aus, so war meine Annahme.. (oder &database[n].mark ?)

    Was macht der * an der Stelle und wie ist es mit Arrays und Zeigern?

    Jetzt nicht verwechseln. Beim scanf hast du einen einfachen Tap, von dem du die Adresse willst: database[n].mark ist ein float. Da kannst du mit dem Adressoperator arbeiten.
    Bei passedTest möchtest du das ganze Array angeben.

    Ob du den Zeiger mit dem * derefernzierst oder mit den Arrayklammern, ist egal.
    Aber beides auf einmal zusammen macht man Doppelzeigern. Den hast du nicht.

    Tergo schrieb:

    Hier der aktuelle Stand:

    Die Änderungen darin sind andere als oben beschrieben und teilweise ein weiterer Rückschritt. 😞



  • Folgender Vorschlag:

    ich verzichte auf die Funktion, diese wird ja auch nicht in der Aufgabe verlangt und beziehe den Test der bestandenen Examen einfach in die main-Funktion mit ein. Das sähe dann so aus (ohne Fehlermeldungen):

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXMATRNUM   6  
    #define MAXEXAMIDNUM    6
    
    struct list
    {                                  
        char Matr[MAXMATRNUM+1];
        char ExamID[MAXEXAMIDNUM+1];
        float mark;
    };
    
    int main()
    {
        int n,i,num=0;
        printf("Please enter the number of exams:\n");
        scanf("%d", &n);
        struct list *database;
        database = malloc (n * sizeof(struct list));
        if (database == 0)
            return EXIT_FAILURE;
    
        for(i=1; i<=n; i++)
        {
            printf("Please enter the Matriculation Number %d\n",i);
            scanf("%c",&*database[n].Matr);
            printf("Please enter the Exam-ID %d\n",i);
            scanf("%c",&*database[n].ExamID);
            printf("Please enter the Mark %d\n",i);
            scanf("%f",&database[n].mark);
        }
        for(i=1; i<=n; i++)
        {
        if(database[n].mark<=4.0 && database[n].mark>=0.7)
        {
          num++;
        }
        }
        printf("The number of passed exams is: %d\n", num);
    
        free (database);
        return EXIT_SUCCESS;
    }
    

    Das Programm führt korrekt aus, fragt nach sämtlichen Werten, jedoch: kurz vor der Auswertung, wieviele Examen bestanden haben erhalte ich eine riesige Meldung:

    ***Error in 'main': free(): invalid next size (fast): 0x000... ***

    gefolgt von jeder Menge Informationen bezüglich des Speichers.

    Es muss also etwas mit der free Funktion nicht stimmen..

    Edit: jetzt das Ganze etwas aufgeräumter.



  • Tergo schrieb:

    Es muss also etwas mit der free Funktion nicht stimmen..

    Wenn die nicht ok ist, dann wärst du nicht der Erste, der der das merkt.

    Das Problem liegt bei dir!

    Lass dir mal den Wert von database nach dem malloc und vor dem free ausgeben und vergleiche die Angaben:

    printf("database zeigt auf %p\n": database);
    

    Oder nutze den Debugger.

    Durch dein krudes vermischen von & und * und Nichtbeachtung der Arraygrenzen zerschießt du dir den Zeiger.



  • database zeigt auf 0x1603010

    Das ist fast der Wert, der anschließend als Error herhält:

    *** Error in 'main': free(): invalid next size (fast): 0x0000000001603010 ***



  • Es kann auch sein, dass du den Infoblock vom Speicherbereich (oder vom Nächsten) überschreibst,
    da du die Arraygrenzen nicht einhälst.



  • Aber würde das nicht bedeuten, wenn ich das Array, sagen wir mal, auf 20 schraube, das es dann definitiv bei einer Zeichenzahl von 6 funktionieren müsste?

    Edit: wenn ich den free-Abschnitt und return EXIT_SUCCESS ersetze mit return 0 funktioniert das Programm.

    Edit2: allerdings ist 'num' am Ende entweder, dass alle Examen bestanden sind oder kein Examen. Tippe ich ein bestandenes und ein nicht bestandenes ein, sagt er, alle hätten bestanden.



  • Tergo schrieb:

    Aber würde das nicht bedeuten, wenn ich das Array, sagen wir mal, auf 20 schraube, das es dann definitiv bei einer Zeichenzahl von 6 funktionieren müsste?

    Es geht um den Speicher mit malloc.

    Wenn ein Array 3 Elemente hat, dann geht der Index von 0 bis 2.

    Bei

    int n = 3
    int *data = malloc(n*sizeof(int));
    

    Dann darfst du auf data[n] (data[3]) nicht zugreifen, da es außerhalb des Speicherbereichs liegt.

    Schau mal, welchen Index du beim printf ausgibst und welchen du beim scanf hast.
    Welche Werte haben bei dir i und n?

    Tergo schrieb:

    Edit: wenn ich den free-Abschnitt und return EXIT_SUCCESS ersetze mit return 0 funktioniert das Programm.

    Das kaschiert nur den Fehler. Er ist trotzdem noch vorhanden.
    Das ist so, als ob du die Motorwarnleuchte am Auto zuklebst, damit du das blinken nicht siehst.



  • Das heißt, ich habe den Index in der for-Schleife falsch angegeben?
    Dann müsste der von i=0 bis i<n gehen, richtig?

    Somit würde jetzt hier auch nicht mehr database[n]... gescannt werden, sondern database[i]..., was mir jetzt erst aufgefallen ist.

    Edit: jetzt tritt der riesige Fehler nicht mehr auf!

    Edit2: allerdings wird nun das Abfragen der Matriculation Number übersprungen.



  • Tergo schrieb:

    Das heißt, ich habe den Index in der for-Schleife falsch angegeben?
    Dann müsste der von i=0 bis i<n gehen, richtig?

    Somit würde jetzt hier auch nicht mehr database[n]... gescannt werden, sondern database[i]..., was mir jetzt erst aufgefallen ist.

    Edit: jetzt tritt der riesige Fehler nicht mehr auf!

    Ich glaube du hast bisher alles falsch gemacht, was man in C falsch machen kann. 😃



  • Tergo schrieb:

    Edit2: allerdings wird nun das Abfragen der Matriculation Number übersprungen.

    Schau dir mal den Formatstring von scanf an: https://en.wikipedia.org/wiki/Scanf_format_string



  • Ja, da muss ich dir zustimmen 😃
    Habe jetzt die Loops jeweils angepasst und siehe da, das Programm funktioniert. Allerdings erst, wenn ich für die Matr und ExamID nicht %c, sondern %s frage. Bei %c wird Matr einfach übersprungen, bei %s nicht.

    Ich muss doch sowieso %s benutzen, schließlich sind doch Matr und ExamID Strings?

    Edit: Das Programm:

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXMATRNUM   6  
    #define MAXEXAMIDNUM    6
    
    struct list
    {                                  
        char Matr[MAXMATRNUM+1];
        char ExamID[MAXEXAMIDNUM+1];
        float mark;
    };
    
    int main()
    {
        int n,i,num=0;
        printf("Please enter the number of exams:\n");
        scanf("%d", &n);
        struct list *database;
        database = malloc (n * sizeof(struct list));
        if (database == 0)
            return EXIT_FAILURE;
    
        for(i=0; i<n; i++)
        {
            printf("Please enter the Matriculation Number %d\n",i+1);
            scanf("%s",&*database[i].Matr);
            printf("Please enter the Exam-ID %d\n",i+1);
            scanf("%s",&*database[i].ExamID);
            printf("Please enter the Mark %d\n",i+1);
            scanf("%f",&database[i].mark);
        }
    
        for(i=0; i<n; i++)
        {
        if(database[i].mark<=4.0 && database[i].mark>=0.7)
        {
          num++;
        }
        }
        printf("The number of passed exams is: %d\n", num);
    
        free (database);
        return EXIT_SUCCESS;
    }
    


  • DirkB schrieb:

    %c bei scanf mit einem char-Array ist selten sinnvoll.

    Das war auf Seite 2.

    Mit %c liest man genau ein Zeichen ein. Das ist unter Umständen schon das '\n' von der Entertaste.



  • Okay, das sehe ich ein..

    Also ist das Programm nun vollständig.
    Ich danke euch vielmals für eure tatkräftige Unterstützung, ich werde mir eure Ratschläge definitiv zu Herzen nehmen und werde versuchen, bei der nächsten Aufgabe solche dummen Fehler wie falsche Loop-Angaben direkt zu vermeiden. 😞



  • Tergo schrieb:

    Ich muss doch sowieso %s benutzen, schließlich sind doch Matr und ExamID Strings?

    Ja, aber du musst noch die Länge einschränken.

    Und weil Matr und ExamID Strings sind, kann man Ausdrücke wie

    &*database[i].Matr
    

    auch noch ein bisschen verständlicher hinschreiben. 😉



  • Ich dachte die Länge wäre hier bereits durch

    #define MAXMATRNUM 6
    #define MAXEXAMIDNUM 6

    bzw.

    char Matr[MAXMATRNUM+1];
    char ExamID[MAXEXAMIDNUM+1];

    beschränkt.

    Und wie kann ich das umschreiben?



  • Tergo schrieb:

    Ich dachte die Länge wäre hier bereits durch

    #define MAXMATRNUM 6
    #define MAXEXAMIDNUM 6

    bzw.

    char Matr[MAXMATRNUM+1];
    char ExamID[MAXEXAMIDNUM+1];

    beschränkt.

    Das ist scanf aber ziemlich egal. 🙂
    Probier es aus.



  • Meinst du so?

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXMATRNUM   6  
    #define MAXEXAMIDNUM    6
    
    struct list
    {                                  
        char Matr[MAXMATRNUM+1];
        char ExamID[MAXEXAMIDNUM+1];
        float mark;
    };
    
    int main()
    {
        int n,i,num=0;
        printf("Please enter the number of exams:\n");
        scanf("%d", &n);
        struct list *database;
        database = malloc (n * sizeof(struct list));
        if (database == 0)
            return EXIT_FAILURE;
    
        for(i=0; i<n; i++)
        {
            printf("Please enter the Matriculation Number %d\n",i+1);
            scanf("%7s",&*database[i].Matr);
            printf("Please enter the Exam-ID %d\n",i+1);
            scanf("%7s",&*database[i].ExamID);
            printf("Please enter the Mark %d\n",i+1);
            scanf("%f",&database[i].mark);
        }
    
        for(i=0; i<n; i++)
        {
        if(database[i].mark<=4.0 && database[i].mark>=0.7)
        {
          num++;
        }
        }
        printf("The number of passed exams is: %d\n", num);
    
        free (database);
        return EXIT_SUCCESS;
    }
    


  • Tergo schrieb:

    Meinst du so?

    Das ist schon mal ein Anfang. Jetzt musst du nur noch nachforschen, ob scanf nicht auch noch eine 0 anfügt (String-Ende), oder sogar noch ein CR (unter Windows womöglich CR und LF) mit in deinen String kopiert.

    Schreibt scanf mit "%7s" wirklich nur 7, oder sogar 8, 9 oder 10 Zeichen?
    Das musst du jetzt noch rausfinden.



  • #include <stdio.h>
    #include <stdlib.h>
    #define MAXMATRNUM   6  
    #define MAXEXAMIDNUM    6
    
    struct list
    {                                  
        char Matr[MAXMATRNUM+1];
        char ExamID[MAXEXAMIDNUM+1];
        float mark;
    };
    
    int main()
    {
        int n,i,num=0;
        printf("Please enter the number of exams:\n");
        scanf("%d", &n);
        struct list *database;
        database = malloc (n * sizeof(struct list));
        if (database == 0)
            return EXIT_FAILURE;
    
        for(i=0; i<n; i++)
        {
            printf("Please enter the Matriculation Number %d\n",i+1);
            scanf("%7s",&*database[i].Matr);
            printf("%s",database[i].Matr);
            printf("Please enter the Exam-ID %d\n",i+1);
            scanf("%7s",&*database[i].ExamID);
            printf("Please enter the Mark %d\n",i+1);
            scanf("%f",&database[i].mark);
        }
    
        for(i=0; i<n; i++)
        {
        if(database[i].mark<=4.0 && database[i].mark>=0.7)
        {
          num++;
        }
        }
        printf("The number of passed exams is: %d\n", num);
    
        free (database);
        return EXIT_SUCCESS;
    }
    

    Habe versucht, den Wert direkt danach auszuprinten. Wenn ich 6 Ziffern eingebe, kommen 6 Ziffern wieder heraus.

    Edit: habe außerdem entdeckt, dass die Berechnung der bestandenen Examen falsch ist, wenn ich z.B. 2x 0.7 und einmal 4.0 eingebe. Laut Programm hätte nur ein Examen bestanden 😕 Edit: 0.7 ist das Problem.

    Edit2: wenn ich mehr als 7 Stellen eingebe, nimmt scanf die Stellen nicht mehr auf. Es werden nur 7 Stellen vergeben. Also sind wir mit %7s doch gut dabei, oder?


Anmelden zum Antworten