Frage zu malloc (array)

Die einfache Frage ist eig. nur, warum dieses Programm funktioniert:

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

int main()
{
	int wiederholung=15;
	char**test;
	int i;
	test = (char**)malloc(sizeof(char*) * (6));
	for (i=0; i<wiederholung; i++)
	{
		test[i]="\ntest";
	}
	
	for (i=0; i<wiederholung; i++)
	{
		printf("%s", test[i]);
	}
	
	return 0;
}

müsste da nicht eig. ein Speicherzugriffsfehler kommen? Ich vermute ich verstehe malloc noch nicht so wirklich.

Ich dachte ich bestimme so wie viel arrays ich sozusagen zur Verfügung habe, eben 6 warum kann ich dann 15 bestimmen?
 
Du hast hier einen schwerwiegende (Denk-)Fehler drin.

Du allokierst 6*32bit. du schreibst aber nur 15*5 bits zurrück.
(Deswegen funktioniert dein Programm.)
Darüber hinaus solltest du wissen das,obwohl dein Array 2 dimensional ist,
der Array Reihe für Reihe hintereinander allokiert wird, das bedeutet:
Element test[15] ist eigentlich element test[2][0] bei einer Arraybreite von
7 Feldern.

Ansich hast du malloc wunderbar verstanden, es gibt einen pointer auf allocierten Speicher zurrück, was du dann damit machst,ist egal.
Du hast dich in deinem Beispiel dazu entschieden es als 2D-Array handzuhaben.
mfg

sw33t

//edit
Sorry ein Fehler ist mir unterlaufen:
Und zwar:
das Element test[2][0] ist das gleich wie test[0][14],bei einem Array mit einer Breite von 7 Feldern.
Vergesse immer das erst die 1.Zahl in der Feldangabe durchgelaufen und wir und dann die nächste und nicht die letzte zuerst.
 
Zuletzt bearbeitet:
Ich glaube, ich hab das teilweise verstanden

Mein Ziel war es eig. das ich z.b.
Code:
char *test[3];
dann in irgendeinem Zeitpunkt
Code:
test[0]="dies ist ein test";
Code:
test[1]="dies ist noch ein test";
und dann eben durch das zweidimensionale array von jeden einzelnen satz jeden einzelnen buchstaben kontrollieren kann.

Nur hat das array ja jetzt eine bestimmte größe, wie kann ich dieses jetzt erweitern sodass es genau den gleichen nutzen wie vorher hat, und vllt sogar ohne das test[0] und test[1] gelöscht sind?
 
Das kannst du auch in einem 2d-array loesen.
Da die Strings aber unterschiedlich lang sind, wir das evtl unangenehm, wenn du die Maximallänge nicht weisst und/oder Speicherplatz sparen willst.

Ich rate dir zu einer LinkedList oder einer DoublyLinkedList.

Da der Inhalt variable Länge haben soll(nehm ich jetzt mal wegen deinem Beispiel an),brauchst du ungefähr sowas:

PHP:
typedef struct dblinkedlist{
         pdbll precessor;
         pcontent  pcont;
         unsigned int position;
         pdbll successor;
}dbll,*pdbll;

typedef struct container{
          char* beginning;
          unsigned int length;
}content,*pcontent;
Du kannst die typedefs auch weglassen.
Das 2.Struckt ist nur damit du keine Overflows in deinem Inhalt bekommst und total optional.

Das 1.Struct ist genau das was du für dein Problem brauchst.
precessor wird beim 1.Element auf NULL gesetzt und successor beim letzen Element, ansonsten zeigen sie auf ein Element was du zuvor mittels malloc allociert hast.
Dann musst du nur noch Funktionen definieren um durch die LinkedList durchzulaufen.

Alternativ kannst du dir auch unter CPP,wenn du es kannst,die STL anschauen.
Dort sind Listen schon vordefiniert und auch methoden um über sie zu iterieren.


Pseudocode:
PHP:
.........
dbll firstcontainer;
pdbll firstpointer;
content firstelement;
pcontent  pcont;
char myfirstcontent[] = "content";

firstpointer = &firstcontainer;
pcont = &firstelement;
pcont->beginning = myfirstcontent;
pcont->length = sizeof(pcont->beginning);
firstpointer->pcont = pcont;
firstpointer->precessor = NULL;
firstpointer->successor = malloc(sizeof(dbll));
firstpointer = firstpointer->successor;
.........
mfg

sw33t
 
Zuletzt bearbeitet:
Ich vermute ich verstehe noch nicht so ganz, wie das gemeint ist. In wie fern wird in deinem zweiten beispielcode myfirstcontent benutzt. Koenntest du das vllt mir noch mal anhand meines beispiels erklaeren? Waere echt super
 
Ok.
Also:
Die Strucktur dblinkedlist ist in deinem Beispiel eine Zelle im array
char *test[4].
myfirstcontent ist der Inhalt dieser Zelle.
Du kannst in meinem Beispiel auch folgendes benutzen:
PHP:
typedef struct dblinkedlist{
         pdbll precessor;
         char  *pcont;
         unsigned int position;
         pdbll successor;
}dbll,*pdbll;

Allerdings willst du deinen inhalt ja evtl etwas dynamischer einfügen,und da du sowiso speicherplatz für deinen String allokieren musst kannst du das auch so machen oder aber ineiner Strucktur,wie dem 2.Struckt.
Sie es als einen Wrapper für deinen String an.
mfg

sw33t

//edit
der grund wiso ich vorhin da noch Fehler ausgebessert habe ist folgender:
meine Codeschnipsel _SOLLTEN_ lauffähig sein wenn du sie in ein Test-Programm einbaust, spiel einfach mal damit rum.
 
Zuletzt bearbeitet:
Ich versteh leider ein paar Dinge noch nicht. Ich sag vllt mal was ich so vorhatte, vllt gibts da ja doch andere lösungen.

Wenn man z.b. eine Pipe ausliest also sagen wir von dem Befehl "ls", dann lese ich ja mit fgets jeweils eine zeile aus. Ich hätte das gerne so gehabt, das in diesem zweidimensionalen Array einmal die zeile eben mit test[zeile] und mit test[zeile][buchstabe] der buchstabe ausgelesen werden kann. Jetzt weiß ich ja bei dem buffer den ich auslese 1. nie genau wie viel zeilen ich speichern muss und wie viel buchstaben jede zeile hat. Somit muss das array ja dynamisch sein, oder?

Ich kann C allgemein noch nicht so lange, bin eher Python gewöhnt, wie stellt man das denn am besten in C an?

edit: weiß nicht ob ich da vllt schon einen total falschen Ansatz mit den zweidimensionalen array mache
 
Dein Ansatz ist nicht falsch nur unpraktisch weil du ,wie du schon sagtest, die Eingabelänge nicht kennst.
Grundsätzlich nehmen Sprachen wie Python,Perl,Php etc sehr viel arbeit ab, in C musst du alles per Hand machen, auch das gesamte Memory-management, darum geht es ja gerade.

Da du die länge der Zeilen nicht kennst bleibt dir, unter C, kaum eine Alternative, wenn du auf Nummer sicher gehen willst, als jede Zeile Buchstabe für Buchstabe auszulesen,die Grösse zu überprüfen,ggf.neuen Speicher zu allockieren und den Speicherblock umkopieren.
(Pass hier auf Memoryleaks auf)

Sobald du ein "\n" triffst weisst du das deine Zeile zu Ende ist, und nun hast eine Grösse,und einen Pointer auf diese linkedlist.
Jetzt kannst du diesen Pointer einfach umbiegen auf deine Strucktur welche die Zeilen enthält oder, du kannst die LinkedLIst mit dem Inhalt auslesen und verkleinert/komprimiert in einen neuen allokierten Speicherbereich packen und den Pointer davon dann umbiegen, je nachdem was für deinen Algorithmus performanter ist.

//edit
Das funktioniert im Grunde wie die Sache mit den Struckturen oben.
Alternativ kannst du dich, wenn du Zeit hast, mal mit Pointerarithmetik beschäftigen,dann geht das alles noch etwas kompakter, aber eins nach dem anderen.
 
Zuletzt bearbeitet:
Du allokierst 6*32bit. du schreibst aber nur 15*5 bits zurrück.
(Deswegen funktioniert dein Programm.)

Das sehe ich nicht so.

Er alloziert Platz für 6 Pointer. (Im prinzip egal ob dort *char, *int oder *void steht)
Auf einer einer 32-Bit Maschine wären das also 6*32Bit = 24 Byte.

Ab test[0] wären also linear 24 Byte reserviert zum beschreiben. Platz für 6 Pointer.

test[2] wäre beispielsweise aber schon *(test + sizeof(*void) * 2).
Und das wäre test[0] + 8 chars (Byte).

test[5] wäre die letzte Zelle für einen Pointer, die man beschreiben darf, also
*(test + sizeof(*void) * 5). => test[0] + 20 chars (Byte). Ab da an bleiben also genau 4 Byte für den letzten Pointer übrig, den man schreiben darf.

Das schreiben auf test[6] ist schon ein Buffer-Overflow.

Warum es trotzdem bis 15 funktioniert? Wenn man Speicherplatz beim Betriebsystem anfordert, dann haben die zurückgegebenen Speicherseiten eine bestimmte Granularität. Man bekommt notwendigerweise dann mehr zurück als man braucht.
Allerdings merkt sich der Speichermanager das, in etwa: "Ich habe 2000 Speicherzellen Platz bekommen. Angefordert wurden aber nur 24. Beim nächsten mal benutze ich wieder Zellen von diesen 2000, aber erst NACH den reservierten 24."

Angenommen du würdest jetzt noch einmal Speicherplatz reservieren. Dann würdest wahrscheinlich (kommt auf die Logik des Speichermanagers und die Umstände an) Platz ab test[6] bekommen, und würdest deine alten char*-Pointer die du in der Schleife geschrieben hast wieder überschreiben mit neuen Daten.

Du hast also Glück, dass es nicht abstürzt, weil mehr kongruent nacheinanderliegener Platz da ist, und du danach nicht noch einmal Speicher anforderst, der ja ab test[6] liegen könnte.

C hat kein Range-Checking wie zum Beispiel Delphi es hat. du kannst test[53453] probieren und es würde freudig ausgewertet werden und nach der Adress-Rechnung wird auch tatsächlich versucht dahin zu schreiben, ohne zu gucken ob das geht. (Eine Range-Checking Logik gibt es in C nicht und wäre auch viel zu Overkill für simples leichtes C, das möglichst portabel sein sollte)


Zu deinem Problem:
Du könntest Zeilen, wie du es schon tatest, als Liste von String-Pointern allozieren:

int alloc_size = 5;
test = (char**)malloc(sizeof(char*) * (5));

Kommen zu den Zeilen wieder welche dazu, kannst du ja wieder mit realloc um 5 erhöhen:
alloc_size += 5;
test = (char**)realloc(sizeof(char*) * (alloc_size));

Dann müsstest du die Länge der Zeile herausfinden und für jede Zeile muss wieder ein malloc her!
Und zwar dieser Art:
test[0] = (char*)malloc(len+1); //wobei len die Länge der Zeile ist

Dann kanst du auch mit strcpy(test[0], sourcestring) deine Strings dortreinkopieren.
 
Zuletzt bearbeitet:
shice stimmt,
hab vergessen,als ich meinen fehler mit test[0][14] ==test[2] != test[14] bemerkt habe,die schlussfolgerungen auch zu ändern.
danke.
mfg

sw33t

//edit und er schreibt 15*6 bits zurrück hab den "\0"-Character vergessen *grummel*
 
Zuletzt bearbeitet:
Vielen Dank euch beiden, das meiste was du ATH0 noch geschrieben hattest, ist wirklich ziemlich kompliziert, das muss man sich öfters durchlesen, sowieso ist C wie ich schon gemerkt habe in diesem Thema ziemlich kompliziert...

Kommen zu den Zeilen wieder welche dazu, kannst du ja wieder mit realloc um 5 erhöhen:
alloc_size += 5;
test = (char**)realloc(sizeof(char*) * (alloc_size));

Dann müsstest du die Länge der Zeile herausfinden und für jede Zeile muss wieder ein malloc her!
Und zwar dieser Art:
test[0] = (char*)malloc(len+1); //wobei len die Länge der Zeile ist

Ich finde diese Möglichkeit ziemlich gut, denn das mit den dblinkedlist muss ich mir noch einmal näher bringen. Aber trotzdem hat es mir viel geholfen.

Zwei Dinge verstehe ich nun noch nicht so ganz:

Ab test[0] wären also linear 24 Byte reserviert zum beschreiben. Platz für 6 Pointer.
Ich hab das jetzt so verstnanden, dass das nur zutrifft, wenn in jeden Pointer auch nur 4 Chars rein geschrieben werden? Also ich verstehe nicht warum dann 6 Pointer reserviert sind bei 24 Byte?

Und das zweite, wenn ich dann eh auf alles test[?] schreiben kann, was bringt mir dann sozusagen die ganze malloc prozedur? ist das dann noch speicherfreundlich? Ich könnte ja einen kleinen Wert bei malloc angeben und trotzdem alle beschreiben? wie in meinem beispiel?

Zu der linkeslist aktion, hab ich mir nun mal diese Seite angeschaut http://www.ehow.com/how_2056292_create-linked-list-c.html die hat mir schon viel geholfen, aber ich weiß leider nicht (das müsste ich aber eig.), was dieses Zeichen "->" bezweckt. Wusste nicht wie ich danach googlen sollte.
 
Zuletzt bearbeitet:
Ein Pointer sind 32Bit oder 4Byte und 1Byte ist die Speichergrösse eines Chars.
Also sind 4Chars 32Bit.

Du musst den Speicherplatz allokieren damit du ihn benutzen kannst.
Denn sonst darfst du ihn nicht benutzen.
Du arbeitest hier sehr nah mit dem OS zusammen.
Das OS gibt dir einen Virtuellen Arbeitsspeicher, von sagen wir 4Gig, und das macht es für jeden Prozess.

Die Adressen des VM werden dann auf die physikalischen Addressen gemappt, sollten sie irgendwohin zeigen wo dein Prozess keine Zugriffsrechte hat, was wahrscheinlich ist, dann gibt es eine Speicherzugriffsverletzung.

Schreib dir mal ein Pythonskript,führe deine Exe sagen wir mal 1Mio mal aus und speichere immer die Rückgabewerte.
Du wirst mit sicherheit mehrere male eine Fehlermeldung bekommen.



Das Zeichen -> ist eine Dereferenz für Pointer auf Struckturen.
Und es macht das lese und schreiben von Code soooo viel einfacher.
Folgendes Beispiel:

PHP:
......
typedef struct test{
         int *was;
}beg,*pbeg;

beg eins;
pbeg zwei = &eins;
int ha = 3;

eins.was = &ha;

printf("%d\n%d",*zwei->was,*(*zwei).was);//gleiches ergebnis
.......

mfg

sw33t
 
Zuletzt bearbeitet:
Nachdem mir nun einiges klar wurde, habe ich versucht mal dieses Szenario zu basteln:

PHP:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>


typedef struct product_data PRODUCT_DATA;

struct product_data {
int product_code;
char *product_value;
PRODUCT_DATA *next;
};

PRODUCT_DATA *products_head = NULL;
PRODUCT_DATA *products_tail = NULL;
PRODUCT_DATA *newproduct;


int main()
{
	char buffer[200];
	FILE*pipe=popen("ls -la", "r");
	int counter=1;
	while (fgets(buffer, 200, pipe))
	{
		newproduct = malloc(sizeof(PRODUCT_DATA));
		newproduct->product_value = malloc(sizeof(char*));
		
		newproduct->product_code = counter;
		newproduct->product_value = buffer;
		newproduct->next = NULL;
		
		if (!products_head) products_head = newproduct;
		else products_tail->next = newproduct;
		
		products_tail = newproduct;
		counter++;
	}
	

	PRODUCT_DATA *product;
	product = products_head;
	while (product) 
	{
		printf("%d", product->product_code);
		printf("%s", product->product_value);
		product = product->next;
	}
	
	
	while (products_head)
	{
		products_tail = products_head->next;
		free(products_head);
		products_head = products_tail;
	}
	
	
	return 0;
}

Warum in aller Welt, kommt als Ergebniss, für alle Werte die letzte Zeile von ls -la heraus??? Wenn ich es nicht mit dem buffer mache sondern einfach einen manuellen Wert nehme, funktioniert das ganze, warum so nicht? Habe alles mögliche getestet und überprüft, die buffer ausgabe ist ja die richtige, wenn ich mir die anzeigen lasse, sehe ich genau was ich sehen sollte.
 
Du gibst an product_value immer die gleiche Addresse.
Und da du die Werte an der Adresse auch immer veränderst, und nicht einen neuen Buffer anlegst, steht da immer das gleiche drin.
Und die letzte Zeile steht drin weil das die letzte manipulation an buffer ist.

Du musst also noch eine zusätzliche allokierung für deinen Buffer anlegen.
mfg

sw33t
 
Hm, wo muss das jetzt hin und wie sieht das aus?

Ich hätte das jetzt bei der while Schleife von fgets so gemacht

PHP:
buffer = malloc(sizeof(char));

Aber das funktioniert ja nicht wirklich.
Das Prinzip wie ich es jetzt habe, ist das eig. sozusagen in Ordnung?
Wenn ich es danach natürlich noch in ein paar Funktionen verpacke
 
es ist gelinde gesagt sehr in ordnung.
du lernst verdammt schnell.

Das malloc muss in die whileschleife ja, aber du willst
malloc(sizeof(char)*200);
sonst wär das etwas klein.

Du könntest die letzten 2 schleifen noch zusammenfassen, und versuchen den Puffer variable zu gestalte.
aber sonst *thumbs up*.
mfg

sw33t
 
Aber wenn ich das jetzt so benutze:
PHP:
buffer=malloc(sizeof(char)*200);

Dann hab ich beim compilen folgenden error

Code:
incompatible types when assigning to type ‘char[200]’ from type ‘void *’

Das wird ja vermutlich was mit der vorherigen
PHP:
char buffer[200];
Deklaration zu tun haben?
 
explizites casting.

buffer = (char*) malloc(sizeof(char)*200);

Das ist kein Problem da ein Pointer vom Typ void auf einen Adressblock zeigt der genau so gross ist wie der auf den ein Pointer vom Typ char zeigt.

mfg

sw33t
 
Hm, ich weiß nicht was ich falsch mache, es geht damit immer noch nicht:

PHP:
	char buffer[200];
	FILE*pipe=popen("ls -la", "r");
	int counter=1;
	while (fgets(buffer, 200, pipe))
	{
		newproduct = malloc(sizeof(PRODUCT_DATA));
		buffer = (char*) malloc(sizeof(char)*200);
		
		newproduct->product_code = counter;
		newproduct->product_value = buffer;
		newproduct->next = NULL;
		
		if (!products_head) products_head = newproduct;
		else products_tail->next = newproduct;
		
		products_tail = newproduct;
		counter++;
		//printf("%s", buffer);
	}

Gleicher Fehler:

Code:
incompatible types when assigning to type ‘char[200]’ from type ‘char *’
?
 
char buffer[200] in char *buffer = malloc(sizeof(char)*200) ändern?
 
Zurück
Oben