C Verständnisfrage zu Strukturen bzw. "dynamischen" Listen

Don_SyStEmS

Cadet 3rd Year
Registriert
Juni 2009
Beiträge
53
Hallo,

ich sitze gerade an einer Aufgabe, an der ich nicht weiterkomme.

Gefordert ist eine Studentenverwaltung unter Benutzung von Strukturen bzw. Listen.

Ich weiß, CB ist kein Hausaufgabenboard, allerdings möchte ich euch nicht bitten meine Aufgaben zu erledigen, es gibt nur eine Sache, die ich nicht ganz so verstehe.
Code:
typedef struct{
    char name[50];
    int martikelNummer;
    int *next;
} studierende;

Macht es Sinn das in die Main-Funktion reinzuschreiben oder kann ich auch ganz normal außerhalb der Main-Funktion darauf zugreifen und bsp eine Funktion datenEinlesen(&s); aufrufen wobei in der Main dann noch studierende s; stehen würde.

Meine Frage ist jetzt, wie ich diese Liste beliebig erweiterbar mache bzw. wie ich weitere Studenten hinzufügen kann.

Befüllt wird das ja so:
Code:
s.name = CHAR;
s.martikelNummer = INT;

Und weiter? Ich habe dann einen Eintrag, wie füge ich den nächsten hinzu?
Ich muss mit *next arbeiten, jedoch wie Spreche ich mithilfe des aktuellen Zeigers meiner Struktur s den nächsten "freien" Platz an um weitere Studenten hinzufügen zu können?

Vielen Dank schonmal.
 
Code:
studierende * ptr = s.next = malloc(sizeof(studierende));
ptr->name = "..";
oder auch:
Code:
(*ptr).matrikelNummer = ...;
(*ptr).next = NULL;

Logischerweise musst du das ganze dann auch wieder mal mit 'free' freigeben - verstehe bis heute nicht, warum sich Lehrer so auf Listen usw. fixieren, wenn die doch extrem ineffizient im vergleich zu Vektoren/Arrays sind...
 
Dass next vom typ int ist, ist irgendwie doof. Klar, sind alle Zeiger gleich groß
und es gäbe deshalb keine Fehler, aber man muss elendig rumcasten. Sinnvoller
wäre sowas

Code:
typedef struct listpeter_t {
    char *charpeter;
    int intpeter;
    struct listpeter_t *nextpeter;
} listpeter_t;

€: Dann kannst du sowas machen:
Code:
listpeter_t *peter, *petra;
[allockram]
peter->nextpeter = petra;
und so dann die Liste aufbauen. Du iterierst dann über die Liste mit
Code:
peter = peter->nextpeter
und erhältst so jeweils den nächsten Eintrag in der Liste. Dem letzen
Eintrag gibst du dann sinnvollerweise NULL als Wert für nextpeter um
das Ende erkennen zu können.
 
Zuletzt bearbeitet:
Hab ich übersehn^^. Nur um das festzuhalten: sizeof(int) != sizeof(void*)! Das wird nirgends im STANDARD garantiert!
 
Sorry, aber irgendwie muss ich dann einen Denkfehler haben, warum funktioniert das nicht?

Fehler in Zeile 20

Code:
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    typedef struct studierende{
        char name[50];
        int martikelNummer;
        struct studierende *next;
    } studierende;
    
    studierende s;
    
    printf("Name: ");
    gets(s.name);
    printf("Nummer: ");
    scanf("%i", &s.martikelNummer);
    printf("name: %s\nnummer: %i\n", s.name, s.martikelNummer);
    
    *s = s->next;
    
    printf("Name: ");
    gets(s.name);
    printf("Nummer: ");
    scanf("%i", &s.martikelNummer);
    printf("name: %s\nnummer: %i\n", s.name, s.martikelNummer);
    
  system("PAUSE");	
  return 0;
}

[allockram] dürfen wir nicht benutzen.

Unabhängig davon ob ich einen -> oder ein . mache der Fehler bleibt gleich:
20 invalid type argument of `unary *'
 
Code:
*s = s->next;
Moment, was?
Das ist auf jeden Fall ein Fehler! Wenn dann
Code:
studierende * ptr = s.next;
's' ist eine Variable mit 'automatic storage' und kein Zeiger!
'ptr' ist ein Zeiger und muss daher mittels '->' verwendet werden...


Wenn ihr kein malloc und kein free verwenden dürft, dann könnt ihr keine dynamische Liste machen... Wenn es nur darum geht die Liste aufzubauen, wäre es noch möglich das 2. Element auch lokal zu allokieren und die Adresse (operator&) davon bei s.next einzutragen...
 
Zuletzt bearbeitet:
Du solltest nochmal darüber nachdenken was die Operatoren . sowie -> machen.

Das eine ist ein direkter Zugriff, das andere dereferenziert den Pointer so als grobe Umschreibung und Hinweis. Das kannst du nicht einfach gegeneinander austauschen.

Code:
 *s = s->next;

Kann nicht so funktionieren.

Wie hast du den Studierenden s erstellt? Nicht als Pointer.
 
Nagut, dann hoffe ich, dass ich es jetzt einigermaßen richtig habe:

Code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_STUDENTS 1000

int main(int argc, char *argv[])
{
  int students[MAX_STUDENTS];
  int student = 0;
  
  typedef struct studierende{
    char name[50];
    int martikelNummer;
    struct studierende *next;
  } studierende;
  
  while(1 == 1){
    studierende s;
    studierende * ptr = malloc(sizeof(studierende));
    students[student] = ptr;
    printf("Name: ");
    gets(s.name);
    printf("Nummer: ");
    scanf("%i", &s.martikelNummer);
    fflush(stdin);
    printf("Name: %s\tNummer:%i\n", s.name, s.martikelNummer);
    printf("ZUGEHOERIGER POINTER: %i\n\n", *ptr);
    student++;
    
  }
  
  system("PAUSE");	
  return 0;
}

Folgender Code bringt mir folgende Ausgabe:
test_structj4kd7.png


Meine Vermutung ist, dass ich in Zeile 18 noch was verändern muss, damit das richtig abläuft. (sihe gleichbleibender Pointer)

Ansonsten würde ich jetzt auch gerne wissen wie ich wieder auf z.b. den ersten Studenten zugreifen kann. (habe ja den (hoffentlich richtigen) pointer im array gespeichert)
 
Zuletzt bearbeitet:
Lerne, was der *-Operator tut.

Allgemein ist dein Code ziemlicher Murks. Bin aber zu betrunken, ihn erschöpfend zu behandeln.

Zum Beispiel könntest du aucn den Speicher, den du dir mit malloc geholt hast, wieder freigeben.
Und eine Variablendeklaration in einer Schleife? Wir sind hier immer noch bei C oder? Geht das?
 
Zuletzt bearbeitet:
Don_SyStEmS kannst du schon irgendwas objektorientiertes? Nur um nochmal ->, * und . zu erklären:

Code:
Student s1;
s1.name = "Don";

Wenn du jetzt aber einen Pointer auf s1 hast:
Code:
Student* ps;
*ps = s1;

Kannst du nicht mit . direkt auf name zugreifen, sondern es gibt 2 Möglichkeiten:
Code:
ps->name = "Don_Neu"; // Dies ist die kurze Schreibweise und -> ist eine Abkürzung für die 2te Möglichkeit:
[B](*ps).[/B]name = "Don_Neu";

Dh mit -> kommst du an die Elemente eines Objekts wenn das links vom -> nur ein Pointer ist. Meine zweite Version dereferenziert den Pointer durch (*...) und mit . greifst du dann auf das Attribut normal zu, als wäre es kein Pointer.

Meine Begriffe "Objekt" und "Attribut" sind natürlich in C unpassend da es dort keine Klassen gibt denn bei structs greift man nicht auf "Attribute" sondern "Elemente" zu. Es ist aber das selbe von der Syntax her und nur eine Unterscheidung wie man es nennt.
 
Zuletzt bearbeitet:
Code:
while(1 == 1){
//...
    studierende s;
    studierende * ptr = malloc(sizeof(studierende));
    students[student] = ptr;
//...
}

Geiles Memory-Leak :-)
 
Kuddlmuddl: Dass die Codebeispiele grober Mist sind ist dir aber schon klar, oder?
 
kuddlmuddl schrieb:
Don_SyStEmS kannst du schon irgendwas objektorientiertes? Nur um nochmal ->, * und . zu erklären:

Code:
Student s1;
s1.name = "Don"; // <-- PROBLEM #1

Wenn du jetzt aber einen Pointer auf s1 hast:
Code:
Student* ps;
*ps = s1;  // <-- PROBLEM #2

Ich habe in deinem Beitrag mal 2 Problemstellen markiert.

Problem #1: Wenn du im ersten Beitrag in diesem Thread mal auf die Definition des structs studierende schaust, wirst du sehen, daß sich hinter "name" ein character array verbirgt. So wie du das da gemacht hast, kann man in C einem Array keine Werte zuweisen (wäre "name" lediglich ein Zeiger auf char, würde es funktionieren). Entweder jedem Element des arrays einzeln einen Character-Wert zuweisen oder mit strcpy() / strncpy() oder einer seiner unzähligen Varianten arbeiten.

Problem #2: Mit *ps dereferenzierst du einen Pointer, dem du nie einen Initialwert zugewiesen hast (das heißt, der kann im Moment sonst wohin zeigen). Das produziert wahrscheinlich einen Absturz.

Möglicherweise hast du einige Details ausgelassen, weil es dir in erster Linie darum ging, Pointer zu erklären, aber wenn du einem Neuling Codeschnipsel zeigst, ist es immer empfehlenswert, wirklich 100%-ig korrekten Code zu präsentieren, sonst verursachst du nur unnötige Verwirrung.
 
Zuletzt bearbeitet:
Sorry falls ich wen verwirrt habe.. ich habs natürlich nicht sorgfältig genug korrektur-gelesen.
Mir gings nur darum zu erklären, dass -> eine Abkürzung für (*...). ist. Das zweite finde ich für Leute die aus der OO Welt kommen deutlich einfacher zu verstehen wenn sie das erste mal mit Pointer arbeiten müssen.
 
Also derzeit passiert ja noch nichts anderes, als dass der Speicher einfach vollgeschrieben wird. Hinterher drauf zugreifen kannst du nicht mehr, da du keinen Pointer auf das erste Element gespeichert hast.

Ich würde so etwas vorschlagen:

Code:
Student *erster = NULL;
Student *letzter = NULL;

while(true)
{
   Student *neuer_student;
  /* Werte für Student einlesen */
 neuer_student->next = NULL; 

 if(erster == NULL)
      erster = neuer_student;
 else
      letzter->next = neuer_student;


letzter = neuer_student;
}

Dann hast du am Ende nämlich noch den Zugriff auf den ersten Eintrag und kansnt somit noch die gesamte Liste benutzen. Mein Code ist jetzt übrigens auch nur so als Idee aufgeschrieben. Da gebe ich absolut keine Garantie, dass alles fertig durchdacht ist. Ich wollte nur aufzeigen, dass du irgendwie das erste Element merken musst.
 
Zurück
Oben