Strukturen in C



  • Sehr geehrte C-Profis,

    brauche eure Hilfe. Ich schreibe das Programm, das die Zeichenketten zeilenweise über stdin (Fileumlenkung) einliest und in dynamisch angeforderte Speicherbereiche kopiert. Zu jeder eingegebenen Zeichenkette muss man eine Struktur mit folgendem Aufbau über dynamische Speicheranforderung anzulegen:

    struct element {
    int nr;
    char *zk;
    };

    nr - fortlaufende Nummer der eingegebenen Zeile
    zk - Pointer auf die Zeichenkette

    Die Pointer auf diese Struktur sind in ein Pointerfeld geeigneter fester Größe zu speichern. Dann muss das Programm die Zeichenketten alphabetisch sortieren. Das Ändern der Reihenfolge soll durch Umordnung von Zeigern auf die zugehörigen Strukturen erfolgen.

    Das Programm habe ich geschrieben und kompiliert. Hier ist der Quelltext:

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    int main() {
    
    char zeichenkette[50];
    int i=0, k=0, zeile=0, sortierung=1;
    size_t laenge=0;
    
    struct zkette {
    	int nr;
    	char *zk;
    } *pointerfeld[100],*zwischenspeicher;
    
    	while(fgets(zeichenkette,50,stdin) != NULL) {
    		laenge = strlen(zeichenkette)-1;
    		pointerfeld[i] = calloc(laenge, sizeof(char));
    		strcpy((*pointerfeld[i]).zk, zeichenkette);
    		zeile++; 
    		(*pointerfeld[i]).nr = zeile;
    
    		i++;
    		}
    
    		k=i;
    
    	while(sortierung != 0) {
    		sortierung=0;
    			for(zeile=0;zeile<i;zeile++) {
    
    			if(strcmp((*pointerfeld[zeile]).zk,(*pointerfeld[zeile+1]).zk)>0) {
    				zwischenspeicher = pointerfeld[zeile];
    				pointerfeld[zeile] = pointerfeld[zeile+1];
    			    pointerfeld[zeile+1] = zwischenspeicher;
    
    				sortierung++;
    				}
    			}
    		i--;
    	}
    
    	i=0;
    
    	while(i <= k) {
    	printf("%d   %s", (*pointerfeld[i]).nr, (*pointerfeld[i]).zk);
    	i++;
    	}
    
    return 0;
    }
    

    Bei der Kompilierung gab es keinen Fehler. Beim Ausführen bekomme ich aber die Meldung:

    "Speicherzugriffsfehler (Speicherabzug geschrieben)".

    Tief in der Seele spüre ich 🙂 , dass ich bei der Speicherplatzzuweisung etwas falsch gemacht habe. Aber, da ich mit C grade angefangen habe, sehe ich den Fehler nicht. Bücher und Internet haben mich bei der Problematik auch nicht weiter gebracht.

    Ich brauche keine vollständige Lösung von euch. Einen kleinen Tipp reicht mir schon.

    LG

    Dennis



  • DeKa_123 schrieb:

    Tief in der Seele spüre ich 🙂 , dass ich bei der Speicherplatzzuweisung etwas falsch gemacht habe...

    Das Gefühl ist ja schonmal richtig 😃

    Visual Studio kompiliert das zwar; wenn man jedoch genau hinsieht wird die
    Zuweisung bei calloc() als Fehler markiert.

    laenge = strlen(zeichenkette)-1; 
    pointerfeld[i] = calloc(laenge, sizeof(char)); 
    strcpy((*pointerfeld[i]).zk, zeichenkette);
    

    Kommentar des Compilers:

    Ein Zeiger vom Typ void* kann keiner Entität vom Typ zkette* zugewiesen
    werden.

    Der Hinweis ist nicht unbegründet ....

    Du benötigst Speicher zu Einen für die Struktur selbst und zum Anderen für
    die Zeichenkette. Das macht man üblicherweise mit zwei malloc oder calloc
    Aufrufen. Aktuell ist es zu wenig Speicher ...

    In der Zeile darunter sieht der strcpy dann auch nicht gesund aus.

    Man würde besser pointerfeld[i]->zk schreiben, aber solange der Pointer ungültig (nicht initialisiert) ist stürzt er hier ohnehin ab.

    Ich hätte zudem auch Zweifel, das strlen() das tut was du vermutest.
    Ich schlage deshalb vor die Doku zu lesen.



  • laenge = strlen(zeichenkette)-1;
    

    das möchte gerne heißen:

    laenge = strlen(zeichenkette)+1;
    

    Vor allem aber allokierst du mit calloc nur Speicher für ein Strukturelementteil und nicht für das gesamte Strukturelement,
    also mind.
    erstmal

    pointerfeld[i] = malloc(sizeof(struct zkette));
    und dann
    pointerfeld[i].zk = malloc(laenge);
    

    Besser wäre aber gleich ein vernünftige Implementierung der Struktur als

    struct zkette {
    int nr;
    char zk[50];
    }
    

    dann brauchst du noch nur ein 1x malloc.



  • Hi there
    Benutze bitte Code-Tages, wenn du Code postest.
    Code markieren und dann den C-Button unter dem 😡 drücken,
    dann sieht das Ergebnis so aus:

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    struct element {
    int nr;
    char *zk;
    }; 
    
    int main() {
    
        char zeichenkette[50];
        int i=0, k=0, zeile=0, sortierung=1;
        size_t laenge=0;
    
        struct zkette {
            int nr;
            char *zk;
        } *pointerfeld[100],*zwischenspeicher;
    
        while(fgets(zeichenkette,50,stdin) != NULL) {
            laenge = strlen(zeichenkette)-1;
            pointerfeld[i] = calloc(laenge, sizeof(char));
            strcpy((*pointerfeld[i]).zk, zeichenkette);
            zeile++;
            (*pointerfeld[i]).nr = zeile;
    
            i++;
        }
    
        k=i;
    
        while(sortierung != 0) {
            sortierung=0;
            for(zeile=0;zeile&lt;i;zeile++) {
    
                if(strcmp((*pointerfeld[zeile]).zk, (*pointerfeld[zeile+1]).zk)&gt;0) {
                    zwischenspeicher = pointerfeld[zeile];
                    pointerfeld[zeile] = pointerfeld[zeile+1];
                    pointerfeld[zeile+1] = zwischenspeicher;
    
                    sortierung++;
                }
            }
            i--;
        }
    
        i=0;
    
        while(i &lt;= k) {
            printf("%d %s", (*pointerfeld[i]).nr, (*pointerfeld[i]).zk);
            i++;
        }
    
        return 0;
    }
    

    Dir fehlt Speicher für den eingelesenen String und für pointerfeld[i] brauchst du genau sizeof( struct zkette ) Bytes Speicher, unabhängig von der Eingabe.
    Die Syntax kannst du deutlich leserlicher, ohne die Klammern und den * Operator schreiben.

    void display_struct_zkette ( struct zkette* p )
    {
    	printf ( "%d %s\n", p->nr, p->zk );
    }
    
    int main() 
    {
    	char* eingabe = "Dies ist eine eingelesene Textzeile.";
    	pointerfeld[0] = malloc ( sizeof ( struct zkette )); 
    	pointerfeld[0]->zk = malloc ( strlen ( eingabe ) + 1 ); // Zugriff ohne Prüfung
    	strcpy ( pointerfeld[0]->zk, eingabe ); // dito.
    	pointerfeld[0]->nr = 1;
    
    	if ( 0 == strcmp ( pointerfeld[0]->zk, eingabe )) // Stringvergleich
    		puts ("Uebereinstimmung!");
    
    	display_struct_zkette ( pointerfeld[0] );
    	// Freigabe des reservierten Speichers, für jedes malloc/calloc ist ein free erforderlich.
    	free ( pointerfeld[0]->zk );
    	free ( pointerfeld[0] );
    	return 0;
    }
    


  • @CJosef u. Wutz
    Ihr seid mit euren Meldungen aber heute etwas verspätet ...

    Ausserdem hatte der Threadstarter den Wunsch

    DeKa_123 schrieb:

    Ich brauche keine vollständige Lösung von euch. Einen kleinen Tipp reicht mir schon.

    Den sollte man doch respektieren, oder ?

    PS: den Hinweis auf Codetags hatte ich vergessen, danke dafür 🙂



  • merano schrieb:

    @CJosef u. Wutz
    Ihr seid mit euren Meldungen aber heute etwas verspätet ...

    Habe ich die maximal zulässige Antwortzeit überschritten?
    Ooojeee!

    merano schrieb:

    Ausserdem hatte der Threadstarter den Wunsch

    DeKa_123 schrieb:

    Ich brauche keine vollständige Lösung von euch. Einen kleinen Tipp reicht mir schon.

    Den sollte man doch respektieren, oder ?

    Wo siehst Du eine vollständige Lösung? 😕

    merano schrieb:

    Kommentar des Compilers:
    Ein Zeiger vom Typ void* kann keiner Entität vom Typ zkette* zugewiesen
    werden.
    Der Hinweis ist nicht unbegründet ....

    Der Hinweis ist 100% unbegründet.


  • Mod

    CJosef schrieb:

    Der Hinweis ist 100% unbegründet.

    Er hat schon einen Grund, bloß nicht den, den merano denkt. Der Grund ist nämlich, dass merano keinen C-Compiler benutzt hat.



  • SeppJ schrieb:

    CJosef schrieb:

    Der Hinweis ist 100% unbegründet.

    Er hat schon einen Grund, bloß nicht den, den merano denkt. Der Grund ist nämlich, dass merano keinen C-Compiler benutzt hat.

    Naja, ein waschechter C++ Compiler würde nicht nur einen Hinweis, sondern einen waschechten Error melden und die Kompilierung abbrechen.
    Ein waschechter C Compiler dagegen hätte nix zu meckern und selbst ein Hinweis wäre 100% unbegründet, darauf bezog ich mich implizit. :p
    Okay klar hast Du Recht, das Ganze umfassender betrachtend, dass es einen Grund für diese Meldung geben muss.
    Der Microsoft Compiler verhält sich da z.B. ganz eigenartig (vllt. benutzt merano diesen), was die Allokierung mit malloc und co betrifft.
    Z.B. bei den folgenden Codezeilen

    #include <stdlib.h>
    struct foo
    {
    	int a;
    };
    int main(void) 
    {
    	struct foo *bar = malloc ( sizeof ( *bar ));
    }
    

    wird im C-Modus, unten im Ausgabefenster der IDE, in dem die Meldungen des Compilers angezeigt werden, bezüglich malloc kein Hinweis und auch keine Fehlermeldung angezeigt. Lediglich im Code-Bereich wird das malloc wie in einer Rechtschreibprüfung 😃 einer Textverarbeitungssoftware rot unterstrichen angemault, als sei es ein Rechtschreibfehler, und wenn man mit der Maus drüber fährt sieht man:

    void* _cdecl malloc(size_t Size)
    Error: Ein Wert vom Typ ""void*"" kann nicht zum Initialisieren einer Entität vom Typ ""foo*"" verwendet werden.

    Kompiliert wird aber trotzdem, trotz des vermeintlichen Errors. 😃
    Guckst Du: http://s7.directupload.net/images/130502/qdwve5b5.png

    Das mag auch die Castfreude vieler Coder erklären, die diesen Compiler benutzten, denn schreibt man

    struct foo *bar = ( struct foo* ) malloc ( sizeof ( *bar ));
    

    ist das rot Unterstrichene weg und alles scheint okay zu sein.

    Sorry, wenn ich wieder verspätet antworte *fg*.



  • Erstens bedanke ich mich für eure Hilfe! Ich habe das Forum zum ersten Mal als Hilfsmittel benutzt, das hat aber super geklappt! Eins möchte ich zu dem Programm schreiben. Ich habe nicht eingegeben, mit welchem Kompiler ich das Programm kompiliert habe. Im Rahmen meines Studiums bin ich dazu gezwungen, C89 zu programmieren und mit gcc zu kompilieren. Fragt ihr mich nicht: "Warum?" Wenn man gcc-Kompiler benutzt und mit Strukturen arbeitet, muss man die Struktur vor der main-Funktion definieren! Sonst ist die Struktur nicht für die "ganze" main-Funktion gültig. So wurde es mir zumindestens heute im Seminar erklärt. Nachdem ich meine Struktur vor main definiert habe, hat das Programm tatsächlich funktioniert. Hier ist nochmal den Quelltext vom Programm, dieses Mal richtig 🙂 :

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    struct zkette {
    	int nr;
    	char *zk;
    };
    
    int main() {
    
    char zeichenkette[50];
    int i=0, k=0, zeile=0, sortierung=1;
    size_t laenge=0;
    
    struct zkette *pointerfeld[100],*zwischenspeicher;
    
    	while(fgets(zeichenkette,50,stdin) != NULL) {
    
    		laenge = strlen(zeichenkette)+1;
    
    		pointerfeld[i] = malloc(sizeof(struct zkette));
    		pointerfeld[i]->zk = malloc(laenge);
    
    		strcpy(pointerfeld[i]->zk, zeichenkette);
    		zeile++; 
    		pointerfeld[i]->nr = zeile;
    
    		i++;
    		}
    
    		k=i-1;
    
    	while(sortierung != 0) {
    		sortierung=0;
    			for(zeile=0;zeile<i-1;zeile++) {
    
    			if(strcmp(pointerfeld[zeile]->zk,pointerfeld[zeile+1]->zk)>0) {
    				zwischenspeicher = pointerfeld[zeile];
    				pointerfeld[zeile] = pointerfeld[zeile+1];
    			    	pointerfeld[zeile+1] = zwischenspeicher;
    
    				sortierung++;
    				}
    			}
    		i--;
    	}
    
    	i=0;
    
    	while(i <= k) {
    	printf("%d   %s", pointerfeld[i]->nr, pointerfeld[i]->zk);
    	i++;
    	}
    
    return 0;
    }
    

    Nochmal vielen Dank :)!

    LG

    Dennis



  • @CJosef: IntelliSense ist in VS immer C++. Der C-Compiler selbst meckert nicht.

    @DeKa_123: Wenn du eine Struktur innerhalb main deklarierst, kannst du sie dort auch verwenden. Global ergibt meist jedoch mehr Sinn, da du den Typ vielleicht zukünftig in einer anderen Funktion verwenden möchtest.

    Probiere es aus, lösche die globale Deklaration und schreib:

    struct zkette {
        int nr;
        char *zk;
    } *pointerfeld[100],*zwischenspeicher;
    

    Das kompiliert ohne Warnungen und Fehler mit gcc -ansi -Wall -Wextra -pedantic a.c und dein Programm funktioniert immer noch.

    Übrigens würde ich i auf < 100 (bzw. sizeof(pointerfeld) / sizeof(pointerfeld[0]) ) in der Eingabeschleife überprüfen, ansonsten kann es passieren, dass dein Programm nach 100 Eingaben abstürzt.



  • Ich danke dir, EinGast, das hat funktioniert. Ich muss die Terminal-Befehle wahrscheinlich besser kennen. Bin doch ein "Windows-Kind", das grade auf Ubuntu umgestiegen ist.


Anmelden zum Antworten