Verkettete Liste

Studentin2009

Grünschnabel
Hallo!

Ich möchte/soll ein Programm schreiben, welches eigenhende Patienten speichert und sie nach der Reihe aufruft. (FirstIn/FirstOut)
Mein Programm müsste zum allergrößten Teil so auch richtig sein, nur leider bricht es sofort ab, sobald ich einen Patienten eingetragen habe.

Es zeigt mir dann an, dass ein Problem bei der Funktion "patient_put" sien soll (unten bei den pointern)

Ich habe schon alles ausprobiert, aber es will einfach nicht funktionieren.

Kann mir jemand von Euch helfen?

Danke schonmal! :)

Grüße Linda

Code:
#include "stdafx.h"
#include "stdlib.h"			
#include "string.h"

char eingabe;

struct Patient_typ
{
	char Geschlecht[5];
	char Nachname[20];
	struct Patient_typ*pNext;
};

struct Queue_typ
{
	struct Patient_typ*pAnker;
	struct Patient_typ*pEnde;
};

struct Queue_typ*pListe;

Queue_typ * konstruktor()		//Neuer Speicherplatz für Patient reservieren
{
	struct Queue_typ*pNew;

	pNew= (Queue_typ*)malloc(sizeof(Queue_typ));
	pNew->pAnker=(Patient_typ*)calloc(1,sizeof(Patient_typ));

	pNew->pEnde=pNew->pAnker;
	
	//return pNew;
}

void patient_put()
{
	struct Patient_typ*pNeuesGlied;
	pNeuesGlied=(Patient_typ*)malloc(sizeof(Patient_typ));
	printf("\nAnkommenden Patienten eintragen:\n");
	printf("\nHerr/Frau + Nachname: ");
	scanf("%s %s",&pNeuesGlied->Geschlecht,pNeuesGlied->Nachname);

	pListe->pEnde->pNext=pNeuesGlied;
	pListe->pEnde=pNeuesGlied;
}

void patient_get()
{
	if(pListe->pAnker->pNext==NULL)
		printf("Kein Patient mehr da!");
	else
	{
	printf("\nNaechster Patient ist:\n");
	printf("\n%s %s",pListe->pAnker->pNext->Geschlecht,pListe->pAnker->pNext->Nachname);

	struct Patient_typ*pfree;
	pfree=pListe->pAnker->pNext;
	pListe->pAnker->pNext=pListe->pAnker->pNext->pNext;
	free(pfree);
	}
}

char menue()
{
	printf("Wartezimerorganisation");
	printf("\n----------------------");
	printf("\nPatient anmelden	(a)");
	printf("\nPatient aufrufen	(r)");
	printf("\nProgramm beenden	(q)\n");

	scanf("%c",&eingabe);
	return eingabe;
}

int _tmain(int argc, _TCHAR* argv[])
{

	Queue_typ * konstruktor();
	while (1)
	{
		eingabe=menue();
		switch(eingabe)
		{
			case 'a': 
				patient_put();
				break;
			case 'r': 
				patient_get();
				break;
			case 'q':
				//exit();
				break;
		}
	}
	return 0;
}
 
Oh, stimmt, das hatte ich übersehen.
Nur leider ändert das ncihts daran das das Programm nicht funktioniert. Er hängt immernoch an der gleichen Stelle. :o(

Aber trotzdem danke!
 
Die pListe wird nicht initialisiert. Meine Vermutung ist, das du im Konstruktor den Pointer pNew nach der erfolgreichen Initialisierung in pListe zu abzulegen?

Außerdem solltest du Sanity-Checks einbauen. Ein malloc() kann fehlschlagen. Du solltest nach dem malloc() den Pointer auf NULL prüfen und das Programm ggf. beenden bzw. die Funktion verlassen. Bevor auf Pointer zugegriffen wird, sollte immer geprüft werden, ob sie NULL sind. Außer bei Pointern auf statische Variablen, die können ja nicht NULL sein:

C++:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *myString = (char*)malloc(20);
    if(myString == NULL)
    {
      printf("Hilfe, mein Pointer is leer!");
      return -1;
    }
    else
    {
      free(myString);
    }

    char anotherString[] = "Hallo Welt";
    char *beginStr = anotherString;
    beginStr++;

    printf("an zweiter Stelle steht %c", *beginStr);

    return 0;
}
 
Um ehrlich zu sein bin ich noch zu sehr am Anfang beim Programmieren, als das ich mit der Antwort wirklich weiterkomme :o(
Könnte mir das jemand nochmal erklären mit der pListe, so das auch ich es vielleicht verstehe? ;-)

Ich glaube übrigens das das mit dem malloc nicht so tragisch ist, da wir es in der Vorlesung nie so gemacht haben und ich jetzt einfach mal davon ausgehe das der Prof es auch ohne diese NULL-Abfrage für richtig hält! :-)

Grüße Linda
 
Jo, Professoren fangen erstmal damit an, zu erklären, wie man es nicht macht, um dann Verwirrung zu stiften, in dem sie zeigen, wie man <sicher> programmiert. Deswegen haben so viele Programme auch so viele Fehler.

Ich erkläre mal kurz, was passiert, wenn du auf einen NULL-Pointer zugreifen willst: Ein Segmentation-Fault oder auch zu deutsch Speicher-Zugriffsfehler. Das kann zunächst vollkommen harmlos sein. Speicherzugriffsfehler können aber auch derart problematisch sein, dass daraus Sicherheitslücken entstehen. Ich empfehle daher, immer vorausschauend zu programmieren. NULL-Checks sind da das mindeste, was man machen kann. Du weißt, was passiert, wenn man Software deployed, die, weil der Programmierer geschlampt hat, abstürzt. Das hinterlässt immer einen faden Beigeschmack.

Nun zu deinem Problem:

C++:
Queue_typ * konstruktor()        //Neuer Speicherplatz für Patient reservieren
{
    struct Queue_typ*pNew;

    pNew= (Queue_typ*)malloc(sizeof(Queue_typ));
    pNew->pAnker=(Patient_typ*)calloc(1,sizeof(Patient_typ));

    pNew->pEnde=pNew->pAnker;
    
    //return pNew;
}

Du hast das return pNew auskommentiert. Warum eigentlich? Kommentier es doch wieder ein und notiere innerhalb der Main-Funktion:

C++:
int _tmain(int argc, _TCHAR* argv[])
{
  pListe = konstruktor();

Das bedeutet folgendes: Beim Starten des Programm wird ja, wie du vielleicht weißt, die Main-Funktion angesprungen. Dort wird zunächst einmal die Funktion konstruktor() aufgerufen. In der Funktion konstruktur() erstellst du einen Zeiger auf eine Query_typ Struktur und forderst für diesen Speicher an. Dann initialisierst du die verkettete Liste. Anschließend gibst du den Zeiger aus der Funktion zurück.

Der Rückgabewert von konstruktor() ist also ein Zeiger auf eine Struktur Query_typ und dieser Zeiger wird dann in pListe eingetragen. Damit ist pListe initialisiert und kann verwendet werden.

Wenn du dann bei Programmende noch dafür sorgen kannst, das sämtliche Zeiger, für die du mit malloc() Speicher angefordert hast, denselben auch wieder freigeben, ist das Programm schon recht gut. Bis auf die Sache mit der Prüfung auf NULL.

Und sag deinem Prof, diese elementaren Dinge sollte man zu allererst lehren. Speicher allokieren, prüfen, benutzen, freigeben. So läuft das nun mal...
 
Hallo!

SUPER DANKE! Das war genau das Problem!
Jetzt habe ich das Programm soweit also tatsächlich ans Laufen gebracht! :o)

Es klappt alles wunderbar, nur ein anderes Problem ist jetzt aufgetaucht... :o(

Wenn ich direkt am Anfang einen Patienten aufrufen will, ohne das einer eingespeichert wurde, kommt richtigerweise ein "Kein Patient vorhanden"
wenn ich aber schon z.B. zwei Patienten gespeichert und wieder aufgerufen habe und somit also keiner mehr im Wartezimmer sitzt...und ich trotzdem versuche einen Patienten aufzurufen, versagt das ganze Programm wieder...
Hat da noch jemand eine Idee?

Grüße Linda

Code:
// Ue11_FIFO.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"
#include "stdlib.h"			//calloc & exit()
#include "string.h"

char eingabe;

struct Patient_typ
{
	char Geschlecht[5];
	char Nachname[20];
	struct Patient_typ*pNext;
};

struct Queue_typ
{
	struct Patient_typ*pAnker;
	struct Patient_typ*pEnde;
};

struct Queue_typ*pListe;

Queue_typ * konstruktor()		//Neuer Speicherplatz für Patient reservieren
{
	struct Queue_typ*pNew;

	pNew= (Queue_typ*)malloc(sizeof(Queue_typ));
	pNew->pAnker=(Patient_typ*)calloc(1,sizeof(Patient_typ));

	pNew->pEnde=pNew->pAnker;
	
	return pNew;
}

void patient_put()
{
	struct Patient_typ*pNeuesGlied;
	pNeuesGlied=(Patient_typ*)malloc(sizeof(Patient_typ));
	printf("\nAnkommenden Patienten eintragen:\n");
	printf("\nHerr/Frau + Nachname: ");
	scanf("%s %s",pNeuesGlied->Geschlecht,pNeuesGlied->Nachname);

	pListe->pEnde->pNext=pNeuesGlied;
	pListe->pEnde=pNeuesGlied;
}

void patient_get()
{
	if(pListe->pAnker->pNext==NULL)
	{
		printf("\nKein Patient mehr da!");
	}
	else
	{
		printf("\nNaechster Patient ist:\n");
		printf("\n%s %s",pListe->pAnker->pNext->Geschlecht,pListe->pAnker->pNext->Nachname);

		struct Patient_typ*pfree;
		pfree=pListe->pAnker->pNext;
		pListe->pAnker->pNext=pListe->pAnker->pNext->pNext;
		free(pfree);
	}
}

char menue()
{
	printf("Wartezimerorganisation");
	printf("\n----------------------");
	printf("\nPatient anmelden	(a)");
	printf("\nPatient aufrufen	(r)");
	printf("\nProgramm beenden	(q)\n");

	scanf("%c",&eingabe);
	return eingabe;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Queue_typ * konstruktor();
	pListe=konstruktor();

	while (1)
	{
		eingabe=menue();
		switch(eingabe)
		{
			case 'a': 
				patient_put();
				printf("\n\nWeiter mit Taste...");
				fflush(stdin);
				getchar();
				system("CLS");
				break;
			case 'r': 
				patient_get();
				printf("\n\nWeiter mit Taste...");
				fflush(stdin);
				getchar();
				system("CLS");
				break;
			case 'q':
				exit(0);
				break;
			default: 
				printf("\nFalsche Eingabe");
				fflush(stdin);
				getchar();
				system("CLS");
				
		}
	}
	return 0;
}
 
Hallo an alle !

Ich habe das Problem gefunden!
Ich habe jetzt alle Speicherplatzreservierungen mit calloc gemacht (vorher war es ja mit calloc und malloc)
Ich habe zwar keine Ahnung wo der UNterschied zwischen calloc und malloc ist, aber wenn ich alles mit calloc mache funktioneirt es ohne Probleme! :o)

Grüße Linda
 
Dein Problem war nur, dass du beim Anlegen eines neuen Patienten in deiner patient_put-Funktion die pNext-Komponente deiner Variable pNeuesGlied nicht mit NULL initialisiert hast, weswegen dort irgendwelcher Müll drinstehen kann, wenn du den Speicher mit malloc alloziierst. Wenn du calloc verwendest, wird der neue Speicher automatisch vom System mit Nullen gelöscht, was deine Abfrage auf NULL dann wieder funktionieren lässt. Ganz sauber ist das allerdings nicht, und ich empfehle dir, die Diskussion in dem Thread Verkettete Liste in C zu studieren. Prinzipiell solltest du dir angewöhnen, jede neu angelegte Zeigervariable mit NULL zu initialisieren, sei es eine eigenständige Zeigervariable oder eine Strukturkomponente.
 
Danke! Das hilft mir wenigstens etwas zu verstehen woran es lag! :o)

Alles bis ins letzte Detail zu verstehen werde ich bis zur Klausur leider eh nicht mehr schaffen, aber da ich um ehrlich zu sein es wahrscheinlich danach auch nie mehr benutzen muss bin ich einfach erstmal froh wenn es überhaupt funktioniert und werde deswegen jetzt immer calloc nehmen! :o)
 
Zurück