Zum Inhalt springen

Anfängerproblem, Speicherzugriffsfehler


Empfohlene Beiträge

Geschrieben

Hi Leute, bin noch ein ziemlicher Anfänger, was das C programmieren angeht. Mache deshalb z.Zt. einige Übungen und da hänge ich z.Zt. an einem Problem. Also erstmal kompiliert es ohne Fehlermeldung durch, aber nach dem Programmaufruf erhalte ich einen Speicherzugriffsfehler.

Der Zweck des kleinen Übungsprogramms ist es, eine Struktur in einer File zu speichern. Man soll aber auch die Möglichkeit haben, die Daten wieder herauszulesen und darzustellen. Später sollen noch Funktionen folgen, die es möglich machen, den gespeicherten Datensatz zu bearbeiten, das kommt aber noch.

Habe den Code in drei Dateien "datensatz.c", "datensatz.h" und "main.c" (die ersten beiden Dateien sollen quasi universell einsetzbar sein, sodass ich mehrere Programme auf Basis der ersten beiden Dateien machen könnte).

Nun also mein Code sieht folgendermassen aus:

"datensatz.h"

#ifndef DATENSATZ_H_

#define DATENSATZ_H_


struct datensatz

{

	char *nachname;

	char *vorname;

	char *strasse;

	char *hausnummer;

	char *plz;

	char *ort;

	char *telefonnummer;

};


int daten_lesen (struct datensatz*, char*);

int daten_schreiben (struct datensatz*, char*);

void daten_ausgeben (struct datensatz*);

void daten_bearbeiten (struct datensatz*); //Die Funktion wird später noch geschrieben, wenn ich das Problem erstmal in den Griff bekommen habe



#endif
"datensatz.c"
#include"datensatz.h"

#include<stdio.h>


int daten_lesen (struct datensatz *ds, char *dateiname)

{

	FILE *lesen;

	int ausg;

	lesen = fopen(dateiname, "r");

	if (lesen == NULL){

		printf("\nDatei konnte nicht gelesen werden!\n");

		ausg = 0;

	} else {

		printf("\nÖffnen erfolgreich\n");

		fgetc(lesen);

		fscanf(lesen, "%s", &ds->nachname);

		fscanf(lesen, "%s", &ds->vorname);

		fscanf(lesen, "%s", &ds->strasse);

		fscanf(lesen, "%s", &ds->hausnummer);

		fscanf(lesen, "%s", &ds->plz);

		fscanf(lesen, "%s", &ds->ort);

		fscanf(lesen, "%s", &ds->telefonnummer);

		fgetc(lesen);

		ausg = 1;

		printf("\nDaten erfolgreich eingelesen\n");

		fclose(lesen);

	};

	return ausg;

}


int daten_schreiben (struct datensatz *ds, char *dateiname)

{

	FILE *schreiben;

	int ausg;

	schreiben = fopen(dateiname, "a+");

	if (schreiben == NULL){

		printf("\nBeim Öffnen oder Erstellen der Datei \"%s\" ist ein Fehler aufgetreten!\n", dateiname);

		ausg = 0;

	} else {

		printf("\nBeginne damit Daten zu schreiben\n");

		fprintf(schreiben, "%s\n", ds->nachname);

		fprintf(schreiben, "%s\n", ds->vorname);

		fprintf(schreiben, "%s\n", ds->strasse);

		fprintf(schreiben, "%s\n", ds->hausnummer);

		fprintf(schreiben, "%s\n", ds->plz);

		fprintf(schreiben, "%s\n", ds->ort);

		fprintf(schreiben, "%s\n", ds->telefonnummer);

		ausg = 1;

		printf("\nDaten erfolgreich geschrieben\n");

		fclose(schreiben);

	};

	return ausg;

}


void daten_ausgeben(struct datensatz *ds)

{

	printf("\n%s\n", ds->nachname);

	printf("%s\n", ds->vorname);

	printf("%s\n", ds->strasse);

	printf("%s\n", ds->hausnummer);

	printf("%s\n", ds->plz);

	printf("%s\n", ds->ort);

	printf("%s\n", ds->telefonnummer);

}


#endif
"main.c"
#include<stdio.h>

#include"datensatz.h"


void main()

{

	struct datensatz ds1;

	ds1.nachname = "Testn";

	ds1.vorname = "Testv";

	ds1.strasse = "Tests";

	ds1.hausnummer = "Testh";

	ds1.plz = "Testp";

	ds1.ort = "Testo";

	ds1.telefonnummer = "Testt";

	struct datensatz ds2;


	daten_schreiben(&ds1, "test.txt");

	daten_lesen(&ds2, "test.txt");

	daten_schreiben(&ds2, "test2.txt");

	//daten_ausgeben(&ds1);

	//printf("\n\nDatensatz 2\n\n");

	//printf("%s\n", *ds2.nachname);

	//daten_ausgeben(&ds2);

}
Also kompiliert wird mit GCC 4.2.3 unter Linux x86 mit folgenden Kommandos:
gcc -c datensatz.c

gcc -c main.c

gcc -o datensatz datensatz.o main.o
Das daraus folgende Programm "datensatz" dient eigentlich nur dazu, die Funktionen zu überprüfen. Genauso mit den vier "printf" Anweisungen in den Funktionen "daten_lesen(..)" und "daten_schreiben(..)" nur zur Funktionskontrolle dienen. Als Ausgabe des Programms erhalte ich folgendes:
>./datensatz


Beginne damit Daten zu schreiben


Daten erfolgreich geschrieben


Öffnen erfolgreich


Daten erfolgreich eingelesen


Beginne damit Daten zu schreiben


Daten erfolgreich geschrieben

Speicherzugriffsfehler

Es steht zwar ein zweites mal da, dass das Schreiben der Datei erfolgreich war, doch befindet sich lediglich in der ersten Datei leserlicher Text in der Form:
Testn

Testv

Tests

Testh

Testp

Testo

Testt

Die zweite Datei ist leer.

Nun vermute ich, dass irgendetwas mit der "daten_lesen(..)" Funtkion nicht stimmt, da ja die "daten_schreiben(..)" Funktion beim ersten mal richtig Funktioniert. Also müsste wohl irgendetwas mit den Daten zwischen dem Auslesen aus der Datei und dem Schreiben in der zweiten Datei passieren.

Ich bedanke mich schoneinmal im Voraus. Wäre sehr toll, wenn mit jemand zumindest einen Anhaltspunkt geben könnte.

Falls ihr nochwas wissen müsst, dann fragts einfach ;)

PS: Stimmt die "daten_ausgeben(..)" Funktion eigentlich so? Also, wenn ich die beiden Fnktionen, in denen die zweite "datensatz" Struktur verwendet wird, auskommentiere und stattdessen "daten_ausgeben(&ds1)" stehen lasse, dann kommt eine Ausgabe. Also sollte das doch funktionieren, oder?

Geschrieben

In deinem Programm fehlt die Speicherverwaltung komplett. Du hantierst hier mit Zeigern, ohne dich auch nur im Geringsten darum zu kümmern, dass die auch auf gültige Speicherbereiche zeigen. In C ist das Bereitstellen des Speichers deine Aufgabe.

Solange du nur mit Stringliteralen hantierst, und nur lesend darauf zugreifst (ds1), geht das noch gut. Danach knallts aber.

Und bei scanf & Co. musst du bei der Verwendung von %s einen Zeiger auf char angeben, nicht dessen Adresse. Dieser Fehler dürfte der Grund sein, warum es nicht sofort beim fscanf knallt. Falsch ist es trotzdem.

Geschrieben

Danke erstmal, dass du mir hilfst. Toll, dass um die Uhrzeit Freitag Nachts noch jemand da ist :D

Nun stellen sich mir aber weitere Fragen. Wie mach ich das mit der Speicherverwaltung. Wie gesagt, bin noch totaler Anfänger. Mit "malloc()" und "free()", oder? OK, dann weiss ich schonmal, womit ich mich beschäftigen muss.

Nun zu dem Kommentar zu "scanf" und Co. Wenn ich den Adressoperator "&" in der "daten_lesen(..)" Funktion entferne, dann kommt der Speicherzugriffsfehler bereits nach der Meldung, dass die Datei erfolgreich geöffnet wurde.

Löst sich das Problem auch, wenn ich mich um eine ordentliche Speicherverwaltung kümmere?

Geschrieben
Wie mach ich das mit der Speicherverwaltung. Wie gesagt, bin noch totaler Anfänger. Mit "malloc()" und "free()", oder?
Richtig.

Nun zu dem Kommentar zu "scanf" und Co. Wenn ich den Adressoperator "&" in der "daten_lesen(..)" Funktion entferne, dann kommt der Speicherzugriffsfehler bereits nach der Meldung, dass die Datei erfolgreich geöffnet wurde.

Durch diesen zweiten Fehler macht sich das fehlende Speichermanagement später bemerkbar. Statt direkt dorthin einzulesen, wo deine uninitialisierten char-Zeiger hinzeigen, liest du dorthin ein, wo diese Zeiger selbst im Speicher liegen. Du schreibst also Text dorthin, wo eigentlich Adressen stehen sollten. Da deine Struktur auf dem Stack liegt, geht das offenbar noch gut - auch wenn es undefiniertes Verhalten ist. Das Programm stürzt aber dann ab, wenn du versuchst, bei der Ausgabe den eingelesenen Text als char-Zeiger zu interpretieren.

Löst sich das Problem auch, wenn ich mich um eine ordentliche Speicherverwaltung kümmere?
Ja.
Geschrieben

Danke für deine Hilfe. Jetzt funktioniert es, so wie ich es wollte.

Habe den Code jetzt folgendermassen abgeändert.

In "datensatz.c" habe ich folgende Funktionen zum anfordern und freigeben von Speicher hinzugefügt:

int daten_anlegen (struct datensatz *ds, char *nname, char *vname, char *str, char *hausnr, char *pl, char *or, char *tel)

{

             int ausg = 1;

	ds->nachname = (char *) malloc((strlen(nname)+1) * sizeof(char));

	strcpy(ds->nachname, nname);

	ds->vorname = (char *) malloc((strlen(vname)+1) * sizeof(char));

	strcpy(ds->vorname, vname);

	ds->strasse = (char *) malloc((strlen(str)+1) * sizeof(char));

	strcpy(ds->strasse, str);

	ds->hausnummer = (char *) malloc((strlen(hausnr)+1) * sizeof(char));

	strcpy(ds->hausnummer, hausnr);

	ds->plz = (char *) malloc((strlen(pl)+1) * sizeof(char));

	strcpy(ds->plz, pl);

	ds->ort = (char *) malloc((strlen(or)+1) * sizeof(char));

	strcpy(ds->ort, or);

	ds->telefonnummer = (char *) malloc((strlen(tel)+1) * sizeof(char));

	strcpy(ds->telefonnummer, tel);

             return ausg;

}


void daten_freigeben (struct datensatz *ds)

{

	free(ds->nachname);

	free(ds->vorname);

	free(ds->strasse);

	free(ds->hausnummer);

	free(ds->plz);

	free(ds->ort);

	free(ds->telefonnummer);

	printf("\nDaten freigegeben\n");

}
und die Funktion zum Auslesen aus der Datei hab ich folgendermassen abgeändert (das dick geschriebene ist neu):
int daten_lesen (struct datensatz *ds, char *dateiname)

{

	[B]char *buffer_nachname = (char *) malloc(51 * sizeof(char));

	char *buffer_vorname = (char *) malloc(51 * sizeof(char));

	char *buffer_strasse = (char *) malloc(51 * sizeof(char));

	char *buffer_hausnummer = (char *) malloc(51 * sizeof(char));

	char *buffer_plz = (char *) malloc(51 * sizeof(char));

	char *buffer_ort = (char *) malloc(51 * sizeof(char));

	char *buffer_telefonnummer = (char *) malloc(51 * sizeof(char));[/B]


	FILE *lesen;

	int ausg;

	lesen = fopen(dateiname, "r");

	if (lesen == NULL){

		printf("\nDatei konnte nicht gelesen werden!\n");

		ausg = 0;

	} else {

		printf("\nÖffnen erfolgreich\n");

		fscanf(lesen, "%s", [B]buffer_nachname[/B]);

		fscanf(lesen, "%s", [B]buffer_vorname[/B]);

		fscanf(lesen, "%s", [B]buffer_strasse[/B]);

		fscanf(lesen, "%s", [B]buffer_hausnummer[/B]);

		fscanf(lesen, "%s", [B]buffer_plz[/B]);

		fscanf(lesen, "%s", [B]buffer_ort[/B]);

		fscanf(lesen, "%s", [B]buffer_telefonnummer[/B]);

		[B]daten_anlegen(ds, buffer_nachname, buffer_vorname, buffer_strasse, buffer_hausnummer, buffer_plz, buffer_ort, buffer_telefonnummer);[/B]

		ausg = 1;

		printf("\nDaten erfolgreich eingelesen\n");

		[B]free(buffer_nachname);

		free(buffer_vorname);

		free(buffer_strasse);

		free(buffer_hausnummer);

		free(buffer_plz);

		free(buffer_ort);

		free(buffer_telefonnummer);[/B]

		fclose(lesen);

	};

	return ausg;

}

Bin jetzt auch wieder um eine Erfahrung reicher^^ Also nochmal vielen Dank.

Falls irgendwer noch nachbesserungsbedarf sieht, immer her mit den Tips :D

Geschrieben

Ein paar Anmerkungen hätte ich noch:

Den Faktor sizeof(char) könntest du weglassen, der ist laut Definition 1. Er schadet natürlich auch nicht, und wenn du dir das so angewöhnst, vergisst du es auch bei anderen Typen nicht so leicht.

Zweitens castest du den Rückgabewert von malloc. Das ist in C nicht notwendig, und kann sogar unter bestimmten Umständen Fehler verdecken. Die (char *)-Casts sollten also besser raus.

Drittens kannst du auf das Semikolon am Ende eines Else-Blocks verzichten ;)

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...