Anfänger und Listen

Schnelle objektorientierte, kompilierende Programmiersprache.
ufor
Beiträge: 11
Registriert: Mo Jan 06, 2014 5:36 pm

Anfänger und Listen

Beitrag von ufor » Mo Feb 17, 2014 1:51 am

Hallo zusammen

Ich bins wiedereinmal mit einigen Fragen. Hab mich durch des Thema verkettete Listen gearbeitet. Dabei ist auch ein Code entstanden der funktioniert. Aber ich will den Code auch verstehen. Und genau da habe ich einige Probleme. Hier mal der komplette Code (im Prinzip aus verschiedenen Tutorials und Bücher zusammengeflickt :-) )

Code: Alles auswählen

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

  struct struktur
  {
  int zahl;
  struct struktur *next;
  };
  struct struktur *first;
  struct struktur *next = NULL;
  
  void datenErfassen ();
  void strukturAnhaengen (int);
  void strukturAusgeben ();
//--------------------------------------------------
  int main ()
  {
  int i = 0;
  for (i = 1; i < 3; i++)
      {
        datenErfassen (i);
      }
  strukturAusgeben ();
  return 0;
  }
//------------------------------------------------
  void datenErfassen (int wert)
  {
     strukturAnhaengen (wert);
   return;
  }
//------------------------------------------------
  void strukturAnhaengen(int wert)
  {

  struct struktur *hilfszeiger;

   if (first == NULL)
      {
       first = malloc(sizeof(*first));
       first -> zahl = wert;

       first -> next = NULL;
      }
   else
      {
      hilfszeiger = first;
       while (hilfszeiger -> next != NULL)
           {
            hilfszeiger = hilfszeiger -> next;
           }
         hilfszeiger -> next = malloc(sizeof(*first));
         hilfszeiger = hilfszeiger -> next;
         hilfszeiger -> zahl = wert;
         hilfszeiger -> next = NULL;
      }
   return;
  }
//--------------------------------------------------
  void strukturAusgeben ()
  {
  struct struktur *lesen;

  lesen = first;

     while (lesen != NULL)
     {
     printf("\n Inhalt von Struktur = %d\n",lesen -> zahl);
     lesen = lesen -> next;
     }
   return;
}
  

In der Funktion strukturAnhaengen gehen meine Probleme los.

Code: Alles auswählen

 while (hilfszeiger -> next != NULL)
           {
            hilfszeiger = hilfszeiger -> next;
           }
          
wiso wird die while schleife nicht durchlaufen und welchen Zweck hat sie dann?

Code: Alles auswählen

 if (first == NULL)
Wieso ist first NULL das wurde doch niergens zugewiesen, aber beim Programablauf wird dieser Programmteil durchlaufen.

Code: Alles auswählen

 hilfszeiger = first;
Auch diese Zuweisung ist mir schleierhaft aber wenn ich sie weglasse gibt es eine Fehlermeldung ( segment default).

Noch ganz allgemein verhalten sich Zeiger des Typs struct anderst als zeiger des Typ int?

Danke schon im Voraus für die Geduld

ufor

Benutzeravatar
naums
Beiträge: 740
Registriert: Sa Jan 02, 2010 10:40 pm
Kontaktdaten:

Re: Anfänger und Listen

Beitrag von naums » Mo Feb 17, 2014 8:21 am

Hallo.

Zeiger sind in C sowieso etwas schwieriger. Sie zeigen nämlich nur dann auf ein Datenobjekt, was du festgelegt hast, wenn du malloc benutzt hast und dort Speicher reserviert hast.

Code: Alles auswählen

struct struktur
  {
  int zahl;
  struct struktur *next;
  };
  struct struktur *first;
  struct struktur *next = NULL;
Das geht so nicht. du legst hier zwei Zeiger struct struktur* an, lässt sie aber nirgendwo hin zeigen. Dh. du kannst hoffen, dass first NULL ist, was in dem Zeiger aber wirklich drin steht weißt du nicht. Wenn du also einen first Zeiger haben willst, dann lass ihn ebenfalls auf NULL zeigen.

Code: Alles auswählen

       first = malloc(sizeof(*first));
Ich bin mir nicht hunerprozentig sicher ob das funktioniert, immerhin derefernzierst du den Zeiger hier; um sicher zu gehen (vor allem wegen dem Problem, dass first am Anfang auf NULL zeigt) 'würde ich hier

Code: Alles auswählen

first=(struct struktur*)  malloc (sizeof(struct struktur)); 
verwenden.

Es gibt, beim Einfügen in eine Liste zwei Fälle: 1. die Liste ist leer. Dann ist auch der Zeiger aufs erste Element (hier first) NULL (wenn du ihn so initialisiert hast). Hast du das herausgefunden, dann hol dir Speicher, lass first darauf zeigen, und fülle das Struct mit deinen Werten. (achte darauf, dass first->next=NULL gesetzt wird)

2. Die Liste ist nicht leer. An eine Liste hängt man im Allgemeinen (es sei denn es gibt ein Sortierkriterium) hinten an. Dh. du musst dir erstmal das Ende der Liste suchen. Du setzt also deinen hilfszeiger=first und lässt ihn sich solange um ein Listenelement weitersetzen, wie hilfszeiger->next!=NULL ist, dh. es weitere Element nach dem aktuellen gibt. Ist hilfszeiger->next irgendwann ==NULL, dann wissen wir, dass wir das letzte Element der Liste erreicht haben. Nun ist es Zeit das neue Element ranzuhängen

Code: Alles auswählen

hilfszeiger->next=(struct struktur*) malloc (sizeof(struct struktur));
hilfszeiger=hilfszeiger->next;
hilfszeiger->wert=<dein Wert>
hilfszeiger->next=NULL
Wichtig ist wieder, dass das next-Member des gerade angehängen Elementes NULL ist. Beachte auch, dass ich hier den hilfszeiger auf das neue Element weitergesetzt habe.

Also, kurze Zusammenfassung: C will, dass du alle Variablen selbst intialisiert. Dh. int c; ist schön, du hast in dem Moment aber keine Ahnung, was in c für ein Wert drin steht. Das gleiche gilt auch für Zeiger. Wenn die einfach noch nirgendwo hinzeigen sollen, dann setz sie gleich NULL (oder nimm den Zahlenwert 0, das ist eigentlich egal, ich bevorzuge NULL). Speicherverwaltung musst du in C ebenfalls selbst machen, willst du also, dass ein Element über das Ende der Funktion hinaus überlebt, dann benutze malloc ( < ANZAHL BYTES > ) und alloziiere soviel Speicher, wie deine struktur braucht. (also idealerweise sizeof(struct struktur);

Hoffe ich konnte helfen.

MfG Naums
.globl truth
truth:
mov r0, #42
mov pc, lr

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Anfänger und Listen

Beitrag von mfro » Mo Feb 17, 2014 8:55 am

naums hat geschrieben:...

Code: Alles auswählen

       first = malloc(sizeof(*first));
Ich bin mir nicht hunerprozentig sicher ob das funktioniert, immerhin derefernzierst du den Zeiger hier; um sicher zu gehen (vor allem wegen dem Problem, dass first am Anfang auf NULL zeigt) 'würde ich hier

Code: Alles auswählen

first=(struct struktur*)  malloc (sizeof(struct struktur)); 
verwenden.
sizeof(*first) ist tatsächlich nicht wirklich falsch, aber _sehr_ mißverständlich.

Auch wenn's so aussieht: hier wird kein Zeiger dereferenziert.

sizeof() ist keine Funktion, sondern ein Operator, der die Größe des übergebenen Datentyps bzw. im Falle einer Variable den Speicherbedarf dieser bestimmt. Hier guckt der Compiler _nicht_ nach, ob der Zeiger tatsächlich auf irgendwas zeigt, sondern nur, auf welchen Datentyp er laut Deklaration zeigen _müsste_ und setzt dessen Größe ein.

*first ist vom Datentyp struct struktur, deswegen wird hier richtigerweise die entsprechende Größe eingetragen, obwohl first tatsächlich auf "nichts" zeigt.

sizeof(struct struktur) ist in jedem Fall besser, weil weniger kryptisch.


Grundsätzlich: ich hab's nicht ausprobiert, bin mir aber sehr sicher, daß jeder halbwegs vernünftiger Compiler mit dem hier gezeigten Code eine ganze Latte Warnungen ausspuckt.

(Nicht nur) für Anfänger gilt die goldene Regel: Warnung = Fehler.

Schaltet alle Warnungen, die der Compiler anbietet ein! (-Wall bei gcc). Jede Warnung sollte ganz genau angeschaut, verstanden und "ausgemerzt" werden. Das ist keine Garantie, daß ein Programm nachher fehlerfrei läuft, aber m.E. eine ganz wesentliche Voraussetzung dafür. Moderne Compiler haben sehr vernünftige Gründe dafür, warum sie bestimmte Warnungen ausgeben. Macht man sich die Mühe, diese Warnungen (und die Gründe dafür) zu verstehen, kann man eine ganze Menge lernen ;).
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Anfänger und Listen

Beitrag von cloidnerux » Mo Feb 17, 2014 10:33 am

Zeiger sind in C sowieso etwas schwieriger. Sie zeigen nämlich nur dann auf ein Datenobjekt, was du festgelegt hast, wenn du malloc benutzt hast und dort Speicher reserviert hast.
Das stimmt so nicht.
Ein Zeiger ist ein Datentyp, der halt auf eine Stelle im Speicher zeigt. Wenn das irgendein zufälliger Wert ist, dann passiert viel unsinniges. Daher muss man zusehen, dass die Adresse auch von was sinnvollen stammt.
Entweder von einer Variable:

Code: Alles auswählen

int *p, i;
p = &i;
oder eben Speicher reservieren:

Code: Alles auswählen

int *p = malloc(sizeof(int));

Code: Alles auswählen

while (hilfszeiger -> next != NULL)
           {
            hilfszeiger = hilfszeiger -> next;
           }
wiso wird die while schleife nicht durchlaufen und welchen Zweck hat sie dann?
Die while schleife wird durchlaufen, solange es noch ein nächstes Element in der Liste gibt.
Dazu muss man wissen, dass "NULL" der Null-Pointer ist. Man weist Pointern, die noch keinen festen Wert haben in der Regel eben NULL zu, damit ein Zugriff auf sie das Programm zum Absturz bringt, denn du darfst auf Adresse 0 nicht zugreifen.
Zudem bietet das eben die Möglichkeit einen Pointer "leer" sein zu lassen, er zeigt halt auf nichts.
In der Schleife wird das ausgenutzt. Solange der Pointer "next", der ja auf das nächste Element auf der Liste zeigt nicht leer ist(!= NULL), solange bist du noch nicht am Ende der liste.
Hat deine Liste noch kein/nur ein Element, dann wird die Schleife nicht durchlaufen.
Wieso ist first NULL das wurde doch niergens zugewiesen, aber beim Programablauf wird dieser Programmteil durchlaufen.
Weil dein Compiler intelligenter war :D
Automatische Initialisierung, deinem Pointer wurde automatisch der Wert NULL zugewiesen, weil keine andere Zuweisung statt fand.
Darauf sollte man sich aber nie verlassen, du solltest first mit NULL initialisieren!
Auch diese Zuweisung ist mir schleierhaft aber wenn ich sie weglasse gibt es eine Fehlermeldung ( segment default).
Du hast in "first" die Information, wo das erste Element liegt. Du willst aber das letzte Element suchen. Dafür musst du dich ja Element für Element durch die Liste arbeiten. Wenn du dazu first verwenden würdest, müsstest du first überschreiben und dann wäre die Information, wo das erste Element liegt, weg.
Den Segmentation fault gibt es wegen der automatischen Initialisierung, "hilfszeiger" ist NULL, du greifst darauf zu, das ist nicht erlaubt und es gibt einen Segmentation fault.
Noch ganz allgemein verhalten sich Zeiger des Typs struct anderst als zeiger des Typ int?
Nope, alle Pointer verhalten sich gleich. Bzw, alle Pointer sind gleich. Ein pointer zeigt auf eine Adresse, vom Typ her ist ein Pointer ein int. Was aber den Pointer anders macht als einen int ist die Kontextualisierung. Dein Compiler behandelt einen Pointer anders.
Auch der Datentyp ist etwas, was nur den Compiler interessiert um zum einen eine Typprüfung durchzuführen, zum anderen um Zugriffe wie

Code: Alles auswählen

hilfszeiger->next
richtig umzusetzten.
In der Hinsicht ist ein Pointer auf ein Strukt etwas anders als ein Pointer auf ein int, du kannst über den Pfeil-Operator auf ein Attribut des Structs zugreifen:

Code: Alles auswählen

struct test myStruct;
myStruct.a = 0;
struct test * p = &myStruct;
p->a = 1;
(*p).a = 1;
Aber wie gesagt, diese Funktionalität kommt durch den Compiler, nicht durch den Datentyp pointer.
Redundanz macht wiederholen unnötig.
quod erat expectandum

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Anfänger und Listen

Beitrag von mfro » Mo Feb 17, 2014 11:55 am

cloidnerux hat geschrieben: Weil dein Compiler intelligenter war :D
Automatische Initialisierung, deinem Pointer wurde automatisch der Wert NULL zugewiesen, weil keine andere Zuweisung statt fand.
Darauf sollte man sich aber nie verlassen, du solltest first mit NULL initialisieren!
Darauf sollte man sich überhaupt gar nie nicht verlassen, weil es schlicht nicht richtig ist.

C-Compiler initialisieren keine Variablen automatisch. In C passiert grundsätzlich nichts, was der Programmierer nicht explizit hingeschrieben hat.

Daß das hier (mehr oder weniger zufällig) funktioniert, hat einen anderen Grund. Die Variable, um die es hier geht, ist eine globale Variable. Globale Variablen legt der Linker im .bss-Segment ab.

Das .bss-Segment ist nicht Bestandteil des Programms, so wie es auf der Platte liegt, sondern wird vom Betriebssystem beim Laden erst angelegt bzw. reserviert. In der Programmdatei steht nur, wie groß es sein soll.

Die meisten Betriebssysteme (aber nicht alle) initialisieren es bei der Gelegenheit mit Nullen und das ist der Grund, warum das mit "first" (aus Versehen) funktioniert und mit "hilfszeiger" zum Beispiel nicht.

Das ist nämlich eine lokale Variable, und die liegt auf dem Stack. Den initialisiert üblicherweise niemand, deswegen steht dort einfach "irgendwas" (ziemlich sicher aber eben keine Null).

Grundsätzlich _muß_ für ein korrekt funktionierendes Programm der erste Zugriff auf eine Variable immer eine Zuweisung sein. Alles andere ist um Probleme gebettelt (der Compiler "weiß" das auch und wirft bei entsprechender Einstellung - s.o. - auch eine Warnung aus).
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Anfänger und Listen

Beitrag von mfro » Mo Feb 17, 2014 3:52 pm

Zum Thema sizeof und "(doch nicht) dereferenzierte Zeiger" noch was:

Code: Alles auswählen

void whatsthis(void)
{
    int *p = NULL;

    int a = sizeof ((int) *p);
    int b = sizeof  (int) *p;
    int c = sizeof p;

    printf("a =  %d, b = %d, c = %d\n", a, b, c);
}
Die Klammern bei sizeof dürfen in C ja weggelassen werden, wenn der Operator nicht mit einem Typ, sondern einer Variable benutzt wird.

Was könnte da wohl ausgegeben werden? Ist a nun dasselbe wie b oder nicht? Geht das überhaupt?

Bitte erst nachdenken, bevor Ihr euren Compiler die Antwort geben läßt!
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Anfänger und Listen

Beitrag von cloidnerux » Mo Feb 17, 2014 4:01 pm

Visual Studio 2010:

Code: Alles auswählen

int b = sizeof  (int) *p;
Fehler	1	error C2297: '*': Ungültig, da der rechte Operand vom Typ 'int *' ist
Wird aber auch als C++ compiliert.
Folgender Code:

Code: Alles auswählen

void whatsthis(void)
{
    int *p = NULL;

    int a = sizeof ((int) *p);
    int b = sizeof  *p;
    int c = sizeof p;

    printf("a =  %d, b = %d, c = %d\n", a, b, c);
}
Produziert:

Code: Alles auswählen

a = 4, b = 4, c = 4
Redundanz macht wiederholen unnötig.
quod erat expectandum

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Anfänger und Listen

Beitrag von mfro » Mo Feb 17, 2014 7:23 pm

cloidnerux hat geschrieben:Visual Studio 2010:

Code: Alles auswählen

int b = sizeof  (int) *p;
Fehler	1	error C2297: '*': Ungültig, da der rechte Operand vom Typ 'int *' ist
Hast Du dir auch überlegt warum das nicht geht?
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Anfänger und Listen

Beitrag von cloidnerux » Mo Feb 17, 2014 7:41 pm

Hast Du dir auch überlegt warum das nicht geht?
Weil Visual Studio in dem Fall nicht dereferenzieren möchte, sondern Multiplizieren:

Code: Alles auswählen

int = int * (int *)
Und Pointer und int will VS nicht so recht Multiplizieren.

Zudem ist der Ausdruck

Code: Alles auswählen

sizeof ((int) *p)
einigermaßen nutzlos.
Du dereferenzierst p...um es dann doch nach int zu casten, egal welchen Typ es vorher hatte.
Redundanz macht wiederholen unnötig.
quod erat expectandum

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Anfänger und Listen

Beitrag von mfro » Mo Feb 17, 2014 8:00 pm

cloidnerux hat geschrieben: Du dereferenzierst p...um es dann doch nach int zu casten, egal welchen Typ es vorher hatte.
Klar. Aber warum ist da offensichtlich ein Unterschied zwischen "((int) *p)" und "(int) *p"? Zwischen "(3 * 2)" und "3 * 2" ist doch auch keiner ;)
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Antworten