PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Großes Struct Array führt zum Speicherfehler



THEReapMan
21-05-2004, 22:40
Hi!
ich hab ein umfangreiches struct (16 Variablen, u.a. Arrays bis zu 300 Spalten)
Dieses Array wird in Abhänigkeit der Zeilen in einer Datei mit struct entry log_entrys[num_entrys]; für den Zugriff eingerichtet. Wenn allerdings num_entrys über eine bestimmte Zahl [~8500] geht, bricht das Programm mit einem Speicherzugriffsfehler ab.

Wie kann ich den Fehler beheben, bzw wie reserviere ich so viel Speicher für das Array richtig (hab mal was von malloc oder so gehört)?

PS: Ein Eintrag belegt ca 1 kb im speicher.

Danke schon mal im Vorraus
Markus

Henni
21-05-2004, 23:04
Kommt darauf an wie dein struct aussieht. Ob es mit dynamischen Arrays (also pointern) oder mit festen Arrays (also nicht pointer ;) ) gefüllt ist.

Bei 2. ist es einfach:

Struct.... *abc=malloc(sizeOf(Struct...));

bei 1. musst du die Arrays des Structs selbst erzeugen also:

Struct.... *abc=malloc(sizeOf(Struct...));
abc->a=malloc(sizeOf(abc->a)*size);

(Den Code hab ich auswendig geschrieben und nach ziemlich langer Programmiersession -> also keine Garantie auf Fehlerfreiheit :D )

Wie gesagt, wenn du uns zeigen würdest was du genau haben willst (Bsp. Code) dann könnte ich dir mehr helfen!

lg, Helmut

THEReapMan
21-05-2004, 23:18
Ok dann hier mal das Struct:


struct entry {
char ip[16];
char requested_file[350];
char filetype[10];
char day[3];
char extension[4];
int month;
char year[5];
char hour[3];
char minutes[3];
char seconds[3];
char browser[200];
char method[8];
char username[25];
char statuscode[4];
char filesize[15];
char redir[30];
};


Zur Zeit sind das alles statische Arrays. Aber wenn ich es hinbekomme sollen die Dynamisch werden, da nicht jedes Array voll gefüllt wird. Aber wie das geht weis ich noch nicht bzw. hab ich noch nicht ganz verstanden

Hier nochn bissl Code wie auf das Struct Array zugeriffen wird:


for(i=0;i<=laenge;i++)
{
log_entrys[num_line].ip[j] = *(garbage+i);
j++;
}


num_line ist eine variable die durch eine Schleife bei jedem durchlauf inkrementiert wird, bis die Anzahl der gesamten Zeilen (num_entrys) ereicht ist.

quinte17
22-05-2004, 08:45
du meinst aber jetzt nicht die arrays IN dem struct??
hoffentlich willst du nur den platz für die structs reservieren, weil sonst wirds schwierig.... *GG*

greetz

THEReapMan
22-05-2004, 09:14
Also was im Struct ist kann ruhig erstma statisch bleiben. das is net so wichtig.
Am wichtigsten ist das ich den Speicher für das Array aus den structs richtig reserviert bekomme. Das Array kann aus meheren tausend Elementen (Structs) bestehen.

Sobalt das Array über ne bestimmte größe geht (~3000) bekomm ich nen "Speicherzugriffsfehler"

Henni
22-05-2004, 22:01
Hmm??

Jetzt kennt man sich noch weniger aus als vorher (ich jedenfalls..)

Also dein derzeitiges Struct Array wurde wie angelegt? (Ich vermute da nämlich, dass du irgendwann über deinem dir zur Verfügung gestellten Bereich hinaus schreibst//liest)

lg, Helmut

Update:

Hab da was überlesen -> sorry.

Das Problem wird sein, dass du keinen Speicherblock dieser Größe auf einmal bekommen wirst (jedenfalls schwer). Bau einmal eine Überprüfung ein ob die Erstellung deines Arrays überhaupt klappt.


if (!my_array)
{
printf("ERROR - Can't create my_array - got NULL Pointer\n");
exit(-1);
}


Sollte es daran scheitern, müsstest du dir eine art dynamisches Array einfallen lassen. (Stichwort Doppelpointer). Ist zwar _etwas_ langsamer aber dafür braucht man keinen so großen Speicherblock auf einmal.

lg, Helmut

THEReapMan
23-05-2004, 16:48
So bin bin wieder n Stück weiter.

So wies aussieht kann entweder der GCC, die i386 Architektur bzw. Linux nicht mit Arrays umgehen die mehr als 8192 Elemente hat.
Das bringt mich in sofern weiter das jetzt mein eingebauter Signalhandler auf den Speicherfehler des Arrays anspringt sobald die Datei weniger als 8192 Zeilen hat.
Wenns mehr sind bleibt der Fehler wie oben.

Es sieht wohl so aus als müsse ich mehrere kleine Array definieren und dann die teile irgendwie durch Zeiger ansprechen.

Wenn es jemanden interessiert, hier der veränderte Code mit Signalhandler:


void mem_error(int sig)
{
printf("Segmention Fault im Modul Apache-Parser\n");
exit(-1);
}
int create_apache_log(struct info *apache)
{
[...]
signal(SIGSEGV, mem_error);
[...]
//Anzahl der Zeilen/Einträge feststellen
while((c = fgetc(logfile)) != EOF)
{
if(c == '\n')
{
num_entrys++;
}
}
num_entrys++;
printf("\t%i Entrys in Logfile\n", num_entrys);

//Strukturarray für die Anzahl der Einträge vorbereiten und Speicher reservieren
struct entry log_entrys[num_entrys];
[...]

panzi
23-05-2004, 18:21
Original geschrieben von THEReapMan
Es sieht wohl so aus als müsse ich mehrere kleine Array definieren und dann die teile irgendwie durch Zeiger ansprechen.

Warum machst nicht gleich ne Liste? Und gibt's die möglichkeit c++ zu verwenden? UNd so könntest std::list bzw. std::vector verwenden.

THEReapMan
23-05-2004, 18:45
C++ könnt ich auch verwenden. nur bin ich da nich so drinne wie in ANSI-C. aber n Buch hab ich auch hier. ich werds mir mal anschaun und eventuell umschreiben.

Wie siehts mit Stringfunktionen in C++ aus? sind die einfacher als in C?

panzi
23-05-2004, 19:42
Original geschrieben von THEReapMan
Wie siehts mit Stringfunktionen in C++ aus? sind die einfacher als in C?

Um Welten!



#include <string>
...
std::string str;

str = "hallo ";
str += "welt";

if( str == "xxx" ) {
// es geht auch !=, <, <=, >, >=
do_something();
}

char * cstr = str.c_str();
...
#include <sstream>
std::ostringstream sstr;

sstr << "hallo" << ' ' << 0.56 << "welt" << endl;
str = sstr.str();

usw. usw. usw.

Anmerkung: man sollte bedenken, das wenn man strings mit + verkettet, immer ein temporäres objekt angelegt wird, welches kopiert und gelöscht wird -> unnötoger overhead. man sollte lieber immer += verwenden oder einen stringstream.

sprich:


std::string str1 = "bla", str2 = "blu", str3;

// schlechter code:
str3 = str1 + str2;

// besserer code:
str3 = str1;
str3 += str2;


Deine struct als ne ganz öffentliche Klasse:


#include <string>
#include <list>

class entry {
public:
std::string ip;
std::string requested_file;
std::string filetype;
std::string day; // warum ist das ein string?
std::string extension;
int month;
std::string year; // warum ist das ein string?
std::string hour; // warum ist das ein string?
std::string minutes;
std::string seconds;
std::string browser;
std::string method;
std::string username;
std::string statuscode; // warum ist das ein string?
std::string filesize; // warum ist das ein string?
std::string redir;

entry( void ) : month( 0 ) {
}

virtual ~entry( void ) {
}
};

// und dann:
typedef std::list< entry > mylist;

// mylist ist dann ne liste die dein entry als entries verwendet ;)

THEReapMan
23-05-2004, 22:04
Day, filesize usw. sind deshalb char-arrays weil ich die umwandlung nioch nicht eingebaut hab als ich den fehler gefunden hab.

Naja was solls. ich hab jetz nen kompletten rewrite in c++ angefangen.

in das System von Klassen und Listen muss ich mich erst reinfitzen

Thomas Engelke
26-05-2004, 08:59
Schade. Eine halbwegs sichere Implementation einer verketteten Liste wäre in kurzer Zeit zu schreiben gewesen. C++ muß da auch tricksen, um die Speicherbereiche zu allokieren, aber da fehlt die Kontrolle, die du bei einer selbstgeschriebenen Lösung hättest.

TME

THEReapMan
27-05-2004, 20:38
Wie mach ich so ne Liste? In meinem Buch steht zwar das es ne rekursive Struktur hat und der Speicher net zusammenhängen muss (Also genau das was ich brauche!), aber wie genau das funktioniert begreif ich einfach net.
könnte mir das einer erklären wie das geht oder vieleicht habt ihr ja auch nen Link zum thema.

Danke schonmal im vorraus!

Markus

[edit] Hab grad gelesen das es in der STL (was auch immer das ist :D ) schon so ne Funktion gibt die das zur verfügung stellt. Nur wie geht das?

chrizel
28-05-2004, 08:03
Ich denke - also jetzt ANSI-C - du machst eine sogenannte "einfach verkettete Liste" oder eine "mehrfach verkettete Liste".

Dazu müsstest du - wie schon gesagt wurde - eigentlich relativ wenig anpassen. Du hast schon eine Structure mit deinen Elementen.

Das besondere von verketteten Listen ist, dass deine Structure einen Pointer auf das nächste Element der selben Structure enthällt. Das letzte Element der Liste enthällt einen Poiner auf NULL.



struct entry; /* Forward deklaration evtl. nötig */

struct entry {
char ip[16];
/* Weitere Elemente... */
struct entry *next; /* Pointer auf das nächste entry-Element! */
}


Das wäre eine einfach verkettete Liste. Eine mehrfach verkettete Liste würde dann noch einen prev-Pointer, also einen Pointer auf das vorherige Element anbieten.

Dann musst du natürlich noch ein paar Verwaltungsroutinen schreiben, die neue Elemente anlegen und die next-Pointer, etc. richtig verknüpfen. Das letzte Element, also das Element das am Ende der Liste ist, hat next auf NULL.

Du könntest das auch mit C++ machen. Es gibt auch schon STL list-Klassen. Ich würd aber eher ANSI-C empfehlen.

THEReapMan
28-05-2004, 10:42
Mhh ja. ich weis noch net ganz wies geht. hat jemand ein tutorial oder sowas für mich? weil ich wills ja lernen und net nur abtippen.

chrizel
28-05-2004, 11:04
Was verstehst du nicht?

Das einzige besondere ist der "Pointer auf sich selbst". Der Rest ist mit logischem Denken zu schaffen. ;)

Ansonsten gib in Google einfach mal "verkettete Listen" ein. Vielleicht findest du ein paar Docs dazu.

Nachtrag: Natürlich solltest du zuvor wissen was Stack und Heap sind. Grundlagen der Speicherverwaltung sollten auch nicht fehlen. Wie du richtig vermuted hast musst du deine Elemente dann natürlich mit malloc anlegen, und mit free zerstören. Vielleicht solltest du also da erst mal ansetzen (Grundlagen der Speicherverwaltung) bevor du dich an verkettete Listen wagst.

Gerade desshalb rate in von C++ ab, weil da die Grundlagen vor dem Benutzer oft versteckt werden. (zumindest wenn du STL list verwendest)

RapidMax
28-05-2004, 18:25
Eine verkette Liste ist hier natürlich die schönste Lösung. Trotzdem will ich nochmals auf das Problem zurückkommen.

Da ist ein relativ grosses Struct mit ca. 600 Byte Grösse. Ein 600 Byte grosser Bereich zu allozieren ist kein Problem.

Das Problem tritt auf, weil ein Array dieser Struct alloziert wird: n*600Byte zu allozieren funktioniert nicht (und ist zudem auch nicht schön).

Du könntest neben der bereits erwähnten (und vermutlich sinnvolleren) Lösung mit der Liste einfach anstelle eines Array of BigStruct ein Array of Pointer to BigStruct allozieren

index = 192;
a = (*BigStruct[])calloc(n, sizeof(BigStruct*));
for (index = 0; index < n; index++) a[index] = NULL;

Anwenden:

if (!a[index]) a[index] = (BigStruct*)malloc(sizeof(BigStruct));
a[index]->field = 42;
...

Zerstören:

for (index = 0; index < n; index++) if (a[index]) free(a[index]);
free(a);

Gruss, Andy