Hackerboard Wiki HaboBlog
Hackerboard bei Facebook Hackerboard bei Google+ Hackerboard bei Twitter

[HaBo]

 
Code Kitchen Allgemeines Coder-Forum rund um das Programmieren eigenständiger, ausführbarer Programme.

Frage zu malloc (array)

Diskussion: Frage zu malloc (array) im Forum Code Kitchen, in der Kategorie Software Home; Die einfache Frage ist eig. nur, warum dieses Programm funktioniert: Code: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> int ...

Antwort
Alt 04.07.10, 16:07   #1 (permalink)
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard 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?
gugugs ist offline   Mit Zitat antworten
Alt 04.07.10, 16:41   #2 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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.

Geändert von sw33tlull4by (04.07.10 um 17:50 Uhr) Grund: Rechtschreibfehler und mir ist ein kleiner Fehler in meiner Erklärung aufgefallen.
  Mit Zitat antworten
   
HaBOT
 

Werbung ist gerade online    
Alt 04.07.10, 18:43   #3 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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?
gugugs ist offline   Mit Zitat antworten
Alt 04.07.10, 19:32   #4 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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-Code:
typedef struct dblinkedlist{
         
pdbll precessor;
         
pcontent  pcont;
         
unsigned int position;
         
pdbll successor;
}
dbll,*pdbll;

typedef struct container{
          
charbeginning;
          
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-Code:
.........
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

Geändert von sw33tlull4by (04.07.10 um 20:00 Uhr) Grund: ein paar kleine fehler gefunden
  Mit Zitat antworten
Alt 04.07.10, 21:52   #5 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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
gugugs ist offline   Mit Zitat antworten
Alt 04.07.10, 21:57   #6 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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-Code:
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.

Geändert von sw33tlull4by (04.07.10 um 22:38 Uhr)
  Mit Zitat antworten
Alt 04.07.10, 22:57   #7 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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
gugugs ist offline   Mit Zitat antworten
Alt 04.07.10, 23:13   #8 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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.

Geändert von sw33tlull4by (04.07.10 um 23:33 Uhr) Grund: Rechtschreibfehler
  Mit Zitat antworten
Alt 05.07.10, 00:34   #9 (permalink)
Member of Honour
 
Benutzerbild von +++ATH0
 
Registriert seit: 02.04.05
+++ATH0 Leistung: Opteron+++ATH0 Leistung: Opteron+++ATH0 Leistung: Opteron+++ATH0 Leistung: Opteron+++ATH0 Leistung: Opteron+++ATH0 Leistung: Opteron
Likes: 222
Standard

Zitat:
Zitat von sw33tlull4by Beitrag anzeigen
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.

Geändert von +++ATH0 (05.07.10 um 00:57 Uhr)
+++ATH0 ist offline   Mit Zitat antworten
Alt 05.07.10, 07:22   #10 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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*

Geändert von sw33tlull4by (05.07.10 um 07:24 Uhr)
  Mit Zitat antworten
Alt 05.07.10, 09:33   #11 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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...

Zitat:
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:

Zitat:
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_crea...ed-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.

Geändert von gugugs (05.07.10 um 10:29 Uhr)
gugugs ist offline   Mit Zitat antworten
Alt 05.07.10, 14:13   #12 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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-Code:
......
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

Geändert von sw33tlull4by (05.07.10 um 14:15 Uhr) Grund: Rechtschreibfehler,wie ich sie hasse
  Mit Zitat antworten
Alt 05.07.10, 14:31   #13 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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

PHP-Code:
#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(buffer200pipe))
    {
        
newproduct malloc(sizeof(PRODUCT_DATA));
        
newproduct->product_value malloc(sizeof(char*));
        
        
newproduct->product_code counter;
        
newproduct->product_value buffer;
        
newproduct->next NULL;
        
        if (!
products_headproducts_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.
gugugs ist offline   Mit Zitat antworten
Alt 05.07.10, 15:08   #14 (permalink)
sw33tlull4by
Guest
 
Likes:
Standard

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
  Mit Zitat antworten
Alt 05.07.10, 15:36   #15 (permalink)
Themenstarter
 
Registriert seit: 25.11.06
gugugs Leistung: Facit NTK
Likes: 0
Standard

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-Code:
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
gugugs ist offline   Mit Zitat antworten
Antwort
   
- Anzeige -

Werbung ist gerade online    

[HaBo] » Software Home » Code Kitchen » Frage zu malloc (array)
Themen-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks sind aus
Pingbacks sind aus
Refbacks sind aus



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62