2D Arrays - stack around the variable 'daten' was corrupted.

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

2D Arrays - stack around the variable 'daten' was corrupted.

Beitrag von forumnewbie » So Mär 09, 2014 11:07 pm

Hi,

ich versuche gerade durch Beispiele die Benutzung von Zeigern bei zweidimensionalen Arrays zu verstehen.
Also experimentiere ich, teste und hoffe, dass ich Zeiger dadurch endlich verstehen werden.

Ich habe eine Matrix/2D-Array vom Typ char, welches ich mit 4 Daten füttern will. Danach sollen die Daten angezeigt werden. Pro Datensatz maximal 50 Zeichen.

Dieses 2D-Array soll an meine zwei Funktionen übergeben werden, die dann diese Aufgaben erledigen. Ich übergebe an die Funktionen mein Array sowie die Anzahl der Datensätze und deren Länge, damit nur im reserviertem Speicher geschrieben wird.

Das Beispiel habe ich ausgedacht und es funktioniert, ABER sobald die Main-Funktion beendet wird, erhalte ich diese Fehlermeldung:
Run-Time Check Failure #2 - Stack around the variable 'daten' was corrupted.
Und ich verstehe einfach nicht, was ich hier falsch mache.

Code: Alles auswählen

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

void einlesen(char**, int, int);
void ausgeben(char**, int, int);

int main(){
	char daten[4][50];
	char *pdaten[4];
	pdaten[0] = daten[0];

	//Matrix mit + beschreiben
	for(int i = 0; i<4; i++)
		for(int j = 0; j<50; j++)
			daten[i][j] = '+';
	
	einlesen(pdaten, 4, 50);
	ausgeben(pdaten, 4, 50);

	printf("\n");
	//system("PAUSE");
	return 0;
}

void einlesen(char **arr, int datenanzahl, int zeichenanzahl)
{
	int start = 0;
	for(int i = 0; i<datenanzahl; i++){
		printf("Datensatz:%d ",i);
		start+=zeichenanzahl; //springt auf den nächsten Datensatz im Speicher
		scanf("%s",(arr[0]+start));
	}
}


void ausgeben(char **arr, int datenanzahl, int zeichenanzahl){
	int start = 0;
	for(int i = 0; i<datenanzahl; i++){
		printf("Datensatz:%d ",i);
		start+=zeichenanzahl; //springt auf den nächsten Datensatz im Speicher
		printf("%s\n",arr[0]+start);
	}
}
Außerdem verstehe ich nicht, warum ich an meine einlesen() und ausgeben()-Funktionen nur meinen Zeiger pdaten und nicht mein Array daten übergeben kann. Wenn ich es mit meinem Array daten versuche, dann erhalte ich dabei

Code: Alles auswählen

einlesen(daten, 4, 50);
folgende Fehlermeldung: char (*)[50]"" ist mit dem Parameter vom Typ ""char **"" inkompatibel

Ich verstehe diese Fehlermeldung nicht - mit dem Zeiger hat es doch funktioniert. Warum ist dann das Array von einem anderen Typ.

Ich gehe davon aus, dass man so etwas viel eleganter und sauberer lösen kann. Aber mir ist es zunächst sehr wichtig zu wissen, was diesen Abstürzt zum Schluss verursacht und warum ich nur einen Zeiger von meinem Array und kein Array selbst an die Funktionen übergeben kann.

Danke!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von Xin » Mo Mär 10, 2014 2:22 am

2D-Arrays sind in C etwas missverständlich.

ein

Code: Alles auswählen

char array[10][20]
ist intern ein 1D-Array mit 200 Einträgen, wobei auf array[x][y] intern array+x*20+y umgerechnet wird.

Wenn Du 2D-Arrays über Zeiger realisieren willst, so muss Du ein Array char * indirectArray[10] erzeugen, also 10 Zeiger, die Du mit 10 Arrays füllst:

Code: Alles auswählen

char * indirect[10];
char direct0[20];
char direct1[20];
...
char direct8[20];
char direct9[20];

indirect[0] = &direct0;
...

indirect[5][5] = 'x';
Es sieht genauso aus, aber hier wird tatsächlich mit Zeigern gearbeitet.
Du kannst natürlich auch mit Schleifen und malloc() Dir die Sache vereinfachen, so dass Du nicht 10 "direkt"-Arrays definieren musst.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

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

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von mfro » Mo Mär 10, 2014 7:56 am

Es hält sich das hartnäckige (aber nichtsdestotrotz falsche) Gerücht, daß Arrays und Zeiger in C immer dasselbe wären.

Code: Alles auswählen

char **x;
ist ein Zeiger auf einen Zeiger auf char. (*x) ist also - genauso wie (z.B. x[2]) ein Zeiger auf ein(en) (oder mehrere) char(s).


Während

Code: Alles auswählen

char y[5][5];
dasselbe wie

Code: Alles auswählen

char z[5 * 5];
(also ein Zeiger auf einen 25 Zeichen langen Speicherbereich) ist.

(*y) ist - und das ist der Unterschied - (genauso wie z.B. y[2] oder auch y[1][3]) hier ein char (und eben kein Zeiger).
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von forumnewbie » Mo Mär 10, 2014 11:20 am

Xin hat geschrieben:2D-Arrays sind in C etwas missverständlich.

ein

Code: Alles auswählen

char array[10][20]
ist intern ein 1D-Array mit 200 Einträgen, wobei auf array[x][y] intern array+x*20+y[/code]umgerechnet wird.
Dass mehrdimensionale Arrays im Computer in einer Dimension gespeichert werden, habe ich bereits verstanden. Die Daten liegen einfach hintereinander im Speicher.
Aus diesem Grund habe ich in meiner Funktion folgendes gemacht:

Code: Alles auswählen

start+=zeichenanzahl; //springt auf den nächsten Datensatz im Speicher
Ein Zeiger braucht ja nur die Start-Adresse, und er springt bei mir beim nächsten Datensatz immer +50 im Speicher und fügt einen neuen hinzu - was auch funktioniert.

Deine Umrechnung array+x*20+y verstehe ich aber nicht. Ich dachte dass char array[10][20] das selbe ist wie char array[10*20]. Im Speicher liegen dann 10x20 Zeichen hintereinander, also 200 Byte. Und ob ich diese 200 Byte als eine oder mehrere Dimensionen betrachte und die Daten dort so ablege, spielt für den Computer keine Rolle. Hauptsache ich komme über die Grenzen von 200 Byte nicht - denn nur diese hat er reserviert.
So verstehe ich das Ganze. Und jetzt nochmal zurück zu den bösen Zeigern. Was ich nicht verstehe, warum bei Zeigern überhaupt Dimensionen wichtig sind?

Warum kann ich z.B. nicht einfach so etwas machen:

Code: Alles auswählen

char daten[4][50];
	char *pdaten;
	pdaten = &daten[0][0];
Die Daten liegen ja sowieso in 1D. Und so lange ich die Startadresse kenne, sowie die Anzahl der Daten und ihre gewünschte Länge, habe ich doch alle Informationen, die ich benötige und kann im Speicher navigieren.

So würde mein Beispiel dann aussehen, welches wieder funktioniert - und erst beim Verlassen der Main-Funktion die selbe Fehlermeldung ausspuckt (die Ein- und Ausgabefunktionen funktionieren einwandfrei).

Code: Alles auswählen

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

void einlesen(char*, int, int);
void ausgeben(char*, int, int);

int main(){
	char daten[4][50];
	char *pdaten;
	pdaten = &daten[0][0];

	//Matrix mit + beschreiben
	for(int i = 0; i<4; i++)
		for(int j = 0; j<50; j++)
			daten[i][j] = '+';
	
	einlesen(pdaten, 4, 50);
	ausgeben(pdaten, 4, 50);

	printf("\n");
	//system("PAUSE");
	return 0;
}

void einlesen(char *arr, int datenanzahl, int zeichenanzahl)
{
	int start = 0;
	for(int i = 0; i<datenanzahl; i++){
		printf("Datensatz:%d ",i);
		start+=zeichenanzahl; //springt auf den nächsten Datensatz im Speicher
		scanf("%s",arr+start);
	}
}


void ausgeben(char *arr, int datenanzahl, int zeichenanzahl){
	int start = 0;
	for(int i = 0; i<datenanzahl; i++){
		printf("Datensatz:%d ",i);
		start+=zeichenanzahl; //springt auf den nächsten Datensatz im Speicher
		printf("%s\n",arr+start);
	}
}

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von Xin » Mo Mär 10, 2014 1:40 pm

forumnewbie hat geschrieben:Deine Umrechnung array+x*20+y verstehe ich aber nicht. Ich dachte dass char array[10][20] das selbe ist wie char array[10*20]. Im Speicher liegen dann 10x20 Zeichen hintereinander, also 200 Byte. Und ob ich diese 200 Byte als eine oder mehrere Dimensionen betrachte und die Daten dort so ablege, spielt für den Computer keine Rolle.
Du sprichst ein Array mit [10*20] mit einer Variablen von 0 bis 199 an, das Array mit [10][20] aber mit zwei Variablen von 0..9 und die zweite von 0..19.
forumnewbie hat geschrieben:So verstehe ich das Ganze. Und jetzt nochmal zurück zu den bösen Zeigern. Was ich nicht verstehe, warum bei Zeigern überhaupt Dimensionen wichtig sind?

Warum kann ich z.B. nicht einfach so etwas machen:

Code: Alles auswählen

char daten[4][50];
	char *pdaten;
	pdaten = &daten[0][0];
Kannst Du.

Ein Zeiger zeigt immer auf genau 1 Element. Dieses eine Element kann aber auch als das erste Element von einer unbekannten Menge verstanden werden.
forumnewbie hat geschrieben:Die Daten liegen ja sowieso in 1D. Und so lange ich die Startadresse kenne, sowie die Anzahl der Daten und ihre gewünschte Länge, habe ich doch alle Informationen, die ich benötige und kann im Speicher navigieren.

So würde mein Beispiel dann aussehen, welches wieder funktioniert - und erst beim Verlassen der Main-Funktion die selbe Fehlermeldung ausspuckt (die Ein- und Ausgabefunktionen funktionieren einwandfrei).
Welche Fehlermeldung?!

Bei mir funktioniert das Programm nicht (keine Ausgabe), aber dafür erhalte ich auch keine Fehlermeldung?

Hier würde ich Dir tatsächlich raten mit Zeigern zu arbeiten. Was Du da tust geht, ist für mich in Ordnung, aber schon etwas beschränkt: Wenn Du mal mehr als 50 Zeichen brauchst, dann bekommst Du ein Problem, das Du nicht hättest, wenn Du die Datenstruktur so definierst, dass Du auf das ersten Buchstaben zeigst ohne zu sagen, wie viele Buchstaben da letztendlich noch folgen.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von forumnewbie » Mo Mär 10, 2014 2:18 pm

Welche Fehlermeldung?!
Die selbe Fehlermeldung wie oben (mein erstes Beispiel), also : "Run-Time Check Failure #2 - Stack around the variable 'daten' was corrupted."

Meine Eingabe und Ausgabe funktionieren:
Datensatz:0 Asdf
Datensatz:1 Ghjk
Datensatz:2 Qwert
Datensatz:3 Uzritoz
Datensatz:0 Asdf
Datensatz:1 Ghjk
Datensatz:2 Qwert
Datensatz:3 Uzritoz
Du sprichst ein Array mit [10*20] mit einer Variablen von 0 bis 199 an, das Array mit [10][20] aber mit zwei Variablen von 0..9 und die zweite von 0..19.
Das verstehe ich. Deshalb habe ich versucht aus 2D ein 1D zu machen, was eigentlich geklappt hat.
Bei mir funktioniert das Programm nicht (keine Ausgabe), aber dafür erhalte ich auch keine Fehlermeldung?
Und warum funktioniert das Programm nicht (bzw. Fehlermeldung in meinem Fall), wenn meine Idee richtig war? Ich arbeite mit Visual Studio Express 2012. Ich habe leider noch nicht verstanden, wo mein Denkfehler liegt.

Dass Zeiger flexibel sind und somit auf ein Element beliebiger Länge im Speicher zeigen können, ist mir auch klar.
Hier würde ich Dir tatsächlich raten mit Zeigern zu arbeiten. Was Du da tust geht, ist für mich in Ordnung, aber schon etwas beschränkt:
In meinem Beispiel arbeite ich aber mit dem Zeiger. Oder meinst du statt
char daten[4][50]; gleich einen Zeiger zu erstellen und den Speicherbereich mit malloc() für ihn reservieren. Also so etwas:

Code: Alles auswählen

char *daten = (char*)(malloc(4*50*sizeof(char)));
bzw.

Code: Alles auswählen

char *daten = (char*)(malloc(200));

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von Xin » Mo Mär 10, 2014 2:38 pm

forumnewbie hat geschrieben:
Welche Fehlermeldung?!
Die selbe Fehlermeldung wie oben (mein erstes Beispiel), also : "Run-Time Check Failure #2 - Stack around the variable 'daten' was corrupted."
Stimmt... ich hatte nur Zahlen eingegeben:

Code: Alles auswählen

xin@prgn:~$ ./a.out
Datensatz:0 Asdf
Datensatz:1 Ghjk
Datensatz:2 Qwert
Datensatz:3 Uzritoz
Segmentation fault
xin@prgn:~$
Schau Dir einlesen mal an und lass vor dem Scanf mal den Wert von arr+start ausgeben. Dann überleg Dir, ob der Dir gefällt.
forumnewbie hat geschrieben:
Hier würde ich Dir tatsächlich raten mit Zeigern zu arbeiten. Was Du da tust geht, ist für mich in Ordnung, aber schon etwas beschränkt:
In meinem Beispiel arbeite ich aber mit dem Zeiger. Oder meinst du statt
char daten[4][50]; gleich einen Zeiger zu erstellen und den Speicherbereich mit malloc() für ihn reservieren. Also so etwas:

Code: Alles auswählen

char *daten = (char*)(malloc(4*50*sizeof(char)));
bzw.

Code: Alles auswählen

char *daten = (char*)(malloc(200));
Ich meine char * daten[4] und anschließend holst Du Dir für die vier Zeiger die Arrays.

Die Idee mit start+=zeichenanzahl funktioniert. Aber Zeigerarithmetik sollte man können. ^^
Und es bleibt eben der Nachteil, dass der Datensatz dann eben genauso groß ist, wie er nunmal ist und sich daran auch nichts mehr so einfach ändern lässt, selbst dann, wenn man auf einmal 80 Zeichen braucht.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

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

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von mfro » Mo Mär 10, 2014 2:54 pm

Meiner Ansicht nach ist das Programm gut geeignet, um Zeigerarithmetik zu lernen, aber nicht unbedingt ein Beispiel für robusten Code. Die "Längenkennungen" für einlesen() und ausgeben() vermitteln den Eindruck einer Flexibilität, die eigentlich nicht vorhanden ist. Wenn Du irgendwo die Feldgröße ändern willst, mußt Du x Zeilen anfassen und vergißt dabei bestimmt mindestens eine ;).

Ich würde das so schreiben:

Code: Alles auswählen

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

#define ANZ     4
#define ZEICHEN 50

void einlesen(char[ANZ][ZEICHEN]);
void ausgeben(char[ANZ][ZEICHEN]);

int main() {
   char daten[ANZ][ZEICHEN];

   // Matrix mit + beschreiben
   for(int i = 0; i < ANZ; i++)
      for(int j = 0; j < ZEICHEN; j++)
         daten[i][j] = '+';

   einlesen(daten);
   ausgeben(daten);

   printf("\n");
   return 0;
}

void einlesen(char arr[ANZ][ZEICHEN])
{
   for (int i = 0; i < ANZ; i++){
      printf("Datensatz:%d ", i);
      scanf("%s", arr[i]);
   }
}

void ausgeben(char arr[ANZ][ZEICHEN]){
   for (int i = 0; i < ANZ; i++){
      printf("Datensatz:%d ", i);
      printf("%s\n", arr[i]);
   }
}
Damit wäre gewährleistet, daß die Feldgrößen immer passen (auch wenn sie mal kleiner oder größer werden sollen) und die ganze Geschichte ist viel einfacher zu lesen.
Wenn einlesen() und ausgeben() mit beliebigen Speichergrößen zurechtkommen sollen, muß die Speicherverwaltung sowieso dynamisch werden und dann sieht das Programm nochmal anders aus.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von forumnewbie » Mo Mär 10, 2014 3:29 pm

@mfro, was du vorgeschlagen hast, war meine Anfangsversion (die ich hier nicht gepostet habe). Die Feldgrenzen habe ich genau so mit dem Makro #define gelöst. Damit entsteht der Eindruck, als ob meine Matrix dynamisch wäre. Ich möchte aber so NICHT haben - also keine Parameter vom Typ eines n-Dimension-Arrays. Deshalb habe ich mein Beispiel umgebaut, weil ich mit dem Zeiger, die auf mehrdimensionale Objekte zeigen, noch große Probleme habe. Mit n-D-Arrays kann ich problemlos hantieren, mit n-D-Zeigern nicht. Ich habe bereits sogar 5-Dimensionen Array erstellt und mit Werten befüllt und ausgegeben. Es hat alles so geklappt, wie ich es mir vorgestellt habe.
Bei Funktionen habe ich aber bis jetzt noch nie ein n-D-Array als Parameter gesehen, sondern da stehen immer Zeiger z.B. char**.

Schau Dir einlesen mal an und lass vor dem Scanf mal den Wert von arr+start ausgeben. Dann überleg Dir, ob der Dir gefällt.
@Xin,
DANKE! Das war mein Denkfehler. Ich muss +50 NACH dem ersten Scanf machen und nicht davor, sonst schreibe ich zum Schluss auf den Speicher, der mir nicht zusteht. Das selbe bei der Printf-Funktion. Außerdem habe ich bei der Initialisierung mit + vergessen am Ende '\0' zu setzen.

Jetzt funktioniert alles einwandfrei und mein Weltbild stimmt jetzt wieder :). Ich habe deinen Rat befolgt und experimentiere jetzt viel rum. Dann kann ich meine Experimente fortsetzen :)

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: 2D Arrays - stack around the variable 'daten' was corrup

Beitrag von Xin » Mo Mär 10, 2014 3:49 pm

forumnewbie hat geschrieben:@Xin,
DANKE! Das war mein Denkfehler. Ich muss +50 NACH dem ersten Scanf machen und nicht davor, sonst schreibe ich zum Schluss auf den Speicher, der mir nicht zusteht. Das selbe bei der Printf-Funktion.
Und viel wichtiger: Du kannst erklären, was Du programmiert hast und warum der Computer dann abschmiert.

Um das noch zu ergänzen: Die lokalen Variablen liegen auf dem Stack. Hinter dem Stack liegt u.a. auch die Adresse (hinter) der Programmstelle, die diese Funktion gerufen hat, damit der Computer am Ende der Funktion dorthin zurückspringen kann. Wenn Du nun über Dein Array hinaus schreibst, überschreibst Du die Rücksprungadresse, das Progamm springt irgendwohin stürzt (meistens) ab. Das Überschreiben der Rücksprungadresse wird gelegentlich aber auch genutzt, um an Stellen zu springen, an denen Programmcode steht, den der Computer dann ausführt. Hierfür eignet sich auch scanf hervorragend. Dieser Programmcode wird nämlich auch gerne mit scanf() eingegeben und leitet einen Hackerangriff ein.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten