Forum: PC-Programmierung Speicherallokation in Klassen-Methoden


von jowi (Gast)


Lesenswert?

Hallo,

ich habe ein Problem. Und zwar ist es mir nicht möglich innerhalb von 
Methoden einer Klasse speicher zu allokieren und diesen sowohl 
Klassenvariablenpointern oder extern deklarierten Pointern zuzuweisen.

Weder new() noch malloc() noch vector::insert() funktionieren. Alle 
bringen den Fehler bad_alloc.

Angaben: C++, Visual Studio 2005.

Hat jemand damit Erfahrung und möglicherweise einen guten Tip, woran es 
liegen könnte.

Vielen Dank! jowi

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Ein vollständiges Beispiel wäre irgenwie nicht ganz verkehrt...

von Matthias H. (experimentator)


Lesenswert?

Hallo,

ich muß gestehen, daß ich nicht 100%ig verstehe, was mit der Zuweisung 
an Klassenpointervariablen gemeint ist. Spätestens dann, wenn eine 
Zuweisung an extern deklarierte Pointer nicht möglich ist, würde mir das 
aber zu denken geben.

Wurde vielleicht versucht, zu viel Speicher zu alloziieren (maximale 
Heapgröße in Projekteinstellungen, ...)? Das kann auch passieren, wenn 
der Heap groß genug, aber stark fragmentiert ist und kein Block 
ausreichender Größe mehr frei ist. Oder ungültiger Parameter für zu 
alloziierende Größe.

Und seit wann wirft malloc(), was eine reine C-Funktion ist, eine 
C++-Exception, es sei denn, man installiert einen eigenen Handler für 
fehlgeschlagene Speicherallokation und wirft die selber?

Ansonsten solltest Du mal den Beispielcode für Dein Problem minimieren, 
möglichst wenige Zeilen, und hier posten, denn "freihändig" kann man zu 
dem Problem wenig sagen.

Tschüß, Matthias

von jowi (Gast)


Lesenswert?

Hallo,

ja das mit dem Beispiel ist relativ. Es funktioniert ja gar nicht.

Beispiel 1:

Main-Funktion:
1
int main() {
2
 vector<string> * writeBuf = new vector<string>;
3
 klasse testklasse;
4
 testklasse.testfunktion(writeBuf);
5
6
}
7
8
void klasse::testfunktion(vector<string> * buf) {
9
 // Füge neuen Leerstring ein, an den später über vector::push_back()
10
 // einzelne chars angefügt werden sollen --> funktionier, wenn ich alles
11
 // in der main schreibe.
12
 // Hier löst die interne Alloc-Funktion ein bad_alloc aus
13
 buf->insert(buf->begin(), "");
14
}

Beispiel 2:

Alles in der Klasse:
1
class klasse() {
2
 // ... Konstruktor, etc....
3
 // Variablen
4
 char * test;
5
6
 // Funktionen
7
 void testfunktion(char * temp);
8
}
9
10
void klasse::testfunktion(char * temp) {
11
 // Nachfolgendes funktioniert beides nicht, obwohl es sich um eine
12
 //Klassenvariable handelt --> bad_alloc
13
 temp = new char(10);
14
 temp = (char*) malloc(10*sizeof(char));
15
}

Beispiel 3:
innerhalb der Methode:
1
void klasse::testfunktion() {
2
 char * temp;
3
 // Funktioniert nicht --> bad_alloc
4
 temp = new char(10);
5
6
 // Funktioniert nicht, liefert NULL-Zeiger
7
 temp = (char*) malloc(10*sizeof(char));
8
}

So, das wären mal einige Beispiele.

Viele Grüße!

von jowi (Gast)


Lesenswert?

Korrigiere: malloc wirft kein bad_alloc, es liefert halt einen 
NULL-Zeiger zurück. Entschuldige!

von Vlad T. (vlad_tepesch)


Lesenswert?

jowi schrieb:
> // Füge neuen Leerstring ein, an den später über vector::push_back()
>  // einzelne chars angefügt werden sollen
häh?
dann hast du einen vector mit strings
[0] = ""
[1] = "a"
[2] = "4"


> --> funktionier, wenn ich alles
>  // in der main schreibe.
macht aber nicht das, was du wahrscheinlich willst.

>  // Hier löst die interne Alloc-Funktion ein bad_alloc aus
das sollte in der Tat nicht passieren.
ist dein rechner vollgemüllt?
linkst du gegen komische runtimes?

jowi schrieb:
> void klasse::testfunktion(char * temp) {
>  // Nachfolgendes funktioniert beides nicht, obwohl es sich um eine
>  //Klassenvariable handelt --> bad_alloc
>  temp = new char(10);
>  temp = (char*) malloc(10*sizeof(char));
> }

was genau erwartest du, was in dieser funktion passiert und was du hast, 
wenn die funktion zurückkehrt?
Es macht überhaupt keinen Sinn einem Input-Pointer neuen Speicher 
zuzuweisen

>  temp = new char(10);
was soll das eigentlich erzeugen?
das folgende malloc lässt vermuten, dass du
temp = new char[10];
meinst.

Tip 1:
starte den Rechner neu, existiert das Speicherproblem noch, installiere 
die eine konsistente Toolchain
Tip 2:
Kaufe dir ein C++ Buch und erarbeite dir die Sprache.

von jowi (Gast)


Lesenswert?

Hallo,

Ok. Entschuldige, dass ich in der Eile runde mit eckigen Klammern 
vertauscht habe. Und ja, jeder fängt mal an und ein C++ Buch habe ich, 
das hilft mir bei diesem Problem aber auch nicht weiter.
1
// Funktion splittet einzelne Protokoll-Zeilen anhand eine Delimiters auf.
2
// Leerzeichen werden hierbei ignoriert. Für jeden Part der Zeile wird in
3
// einem Vektor ein neuer String eingefügt.
4
int bspklasse::splitLine(char delim, char * readBuf, vector<string> *\ writeBuf) {
5
  // Declaration of local variables
6
  int i = 0;    // common counter
7
  int row = 0;  // amount of array-rows with rowWidth chars each
8
9
  // Füge ersten Leerstring hinzu, an den die eingelesenen Chars
10
  // angehängt werden können
11
  writeBuf->insert(writeBuf->begin(),"");
12
13
  while (readBuf[i] != '\r') {
14
    if(readBuf[i] != ' ' || delim == ' ') {
15
      if(readBuf[i] != delim) {
16
        (*writeBuf)[row].push_back(readBuf[i]);
17
      }
18
      else {
19
        // Füge neuen Leerstring in Vektor ein
20
        writeBuf->insert(writeBuf->end(),"");
21
        row++;        
22
      }
23
    }
24
    i++;  // Increment sign-counter
25
  }
26
27
  return 0;
28
}

Die oben aufgeführte Methode erhält von extern ein Delimiter-Zeichen, 
einen char-Pointer auf ein Array, das die zu verarbeitenden Daten 
enthält, sowie einen Zeiger auf einen Vektor, in der die aufgesplittete 
Zeile gespeichert werden soll.
Den Vektor habe ich extern deklariert (in der main) und die 
Speicherallokation ist deswegen nötig, weil diese beim Hinzufügen eines 
neuen Strings automatisch aufgerufen wird.

Viele Grüße, jowi

von jowi (Gast)


Lesenswert?

Ein '\' bei den Übergabeparametern zu viel!

von Vlad T. (vlad_tepesch)


Lesenswert?

so macht das ganze schon mehr sinn

jowi schrieb:
> Den Vektor habe ich extern deklariert (in der main) und die
> Speicherallokation ist deswegen nötig, weil diese beim Hinzufügen eines
> neuen Strings automatisch aufgerufen wird.

und beim insert oder push-back wirft er exceptions?
irgendwas scheint mit deiner Buildumgebung nicht zu stimmen.



Tip:
writeBuf->insert(writeBuf->end(),"");
da kannst du auch
writeBuf->push_back("");
schreiben

Tip:
in c++ benutzt man lieber referenzen anstatt von Pointern.
Dann kannst du dir auch die dereferenzierungen sparen:
...  vector<string>& writeBuf...
.
.
.
  writeBuf.push_back("");
.
.
.
        writeBuf[row].push_back(readBuf[i]);
.
.
.
        writeBuf.push_back("");

von Matthias H. (experimentator)


Lesenswert?

Hallo jowi,

wie Vlad schon angedeutet hat, ist Dein Beispiel 2 sinnlos, da der 
Rückgabewert der Speicherallokation nicht zurückgeliefert wird.

Erst einmal ist es eine gute Idee, die Fehlermeldungen zu analysieren. 
Liefert Dir bad_alloc::what() bzw. bzw. strerror(errno) irgendwas 
sinnvolles?

Hast Du die Projekteinstellungen (Heapgröße) kontrolliert?

Funktionieren andere Projekte, die dynamisch Speicher alloziieren,  in 
der gleichen Umgebung, z. B. beim Compiler mitgelieferte Beispiele?

Compilierst Du mit Debugging und linkst Du mit den Debug-Bibliotheken? 
Das liefert u. U. Fehlermeldungen, wo die "normale" Version einfach 
nicht funktioniert.

Verwendest Du Multithreading und linkst versehentlich mit den 
Single-Threaded-Bibliotheken?

Führst Du an anderen Stellen wilde Pointeroperationen durch, die evtl. 
den Heap beschädigen?

Ein Microsoft-typischer Fehler ist, in einer DLL Speicher zuzuweisen und 
anderswo freizugeben oder umgekehrt - unter UNIX geht das normalerweise 
gut.

Abgesehen davon würde ich Dir empfehlen, für den vektor<string> ein 
typedef zu machen, den Vektor nicht dynamisch zu alloziieren und dann 
per Referenzparameter an Verarbeitungsfunktionen zu übergeben. Das wird 
Dir beim aktuellen Problem vermutlich nichts helfen, ist aber sauberer 
und weniger fehleranfällig.

Tschüß, Matthias

von jowi (Gast)


Lesenswert?

Hallo und vielen Dank erstmal!

Ganz schön viel Input, den ich mal abarbeiten werde.

Kurze Infos noch:
bad_alloc wird bereits von dem ersten insert geworfen. Kopiere ich den 
gesamten Code in die main, in der ich auch die zu übergebenden Parameter 
deklariere, funktioniert der gesamte Code!

Deklarationen:
1
 vector<string> * writeBuf = new vector<string>;
2
 char * readBuf = "Beispiel zum testen\r\n";

Komisch ist halt, dass ich innerhalb der Klassenmethoden in keiner Weise 
Speicher allokieren (oder alloziieren?) kann. Es scheitert immer und ich 
bekomme oben genannten Null-Zeiger oder ein bad_alloc zurück.

Das ist doch sehr komisch, oder? Selbst beim Allokieren von Speicher für 
Pointervariablen der Klasse (z.B. einfach char *) treten diese Fehler 
auf. Hierfür werden ja nichtmal Zeiger von extern übergeben!

PS: Nach dem Neustart des Rechners sofort beim ersten Durchlauf 
dieselben Probleme, es kann also nicht an einem zugemüllten Rechner 
liegen.

Viele Grüße, jowi

von Rolf Magnus (Gast)


Lesenswert?

Kannst du mal ein minimales, aber komplettes Programm posten, das den 
Fehler zeigt?  Dann könnte man eher sagen, ob dein Programm einen Fehler 
hat oder es doch an irgendwas anderem liegt.

von jowi (Gast)


Lesenswert?

Anbei ein gesamtes Beispielprogramm:

Die Klassendefinition wird hierbei nicht mitgeführt, da die Funktion 
keine Variablen der Klasse bearbeitet. Ich weiß, dann könnte man die 
Funktion auch gleich extern deklarieren, aber ich hätte das gern alles 
zusammen. Außerdem funktioniert das reservieren von Speicher ja generell 
nicht innerhalb von Klassen-Methoden, auch wenn ich damit 
Pointervariablen der Klasse bearbeite.

Die Konsolenparameter sind derzeit irrelevant. Diese sind dauerhaft 
unter den Projekt-Eigenschaften/Debug-Optionen gesetzt.

Main-Funktion:
1
int _tmain(int argc, _TCHAR* argv[]){
2
  vector<string>* writeBuf = new vector<string>;
3
  char * readBuf = NULL;// = "Beispiel zum testen\r\n";
4
   
5
  bspklasse client(argv[1]); // Initialisierung eines Objektes. Für unsere Problem-Betrachtung uninteressant!
6
  client.splitLine(' ', readBuf, writeBuf);
7
}

und dann die Methode der Klasse bspklasse:
1
int bspklasse::splitLine(char delim, char * readBuf, vector<string> *writeBuf) {
2
  // Declaration of local variables
3
  int i = 0;    // common counter
4
  int row = 0;  // amount of array-rows with rowWidth chars each
5
6
  // Füge ersten Leerstring hinzu, an den die eingelesenen Chars
7
  // angehängt werden können
8
  writeBuf->insert(writeBuf->begin(),"");
9
10
  while (readBuf[i] != '\r') {
11
    if(readBuf[i] != ' ' || delim == ' ') {
12
      if(readBuf[i] != delim) {
13
        (*writeBuf)[row].push_back(readBuf[i]);
14
      }
15
      else {
16
        // Füge neuen Leerstring in Vektor ein
17
        writeBuf->insert(writeBuf->end(),"");
18
        row++;        
19
      }
20
    }
21
    i++;  // Increment sign-counter
22
  }
23
24
  return 0;
25
}

Mehr ist es ja gar nicht.

Ich hoffe, ihr könnt mir helfen.

PS: Ich kompiliere als Debug; eine Toolchain (soweit ich weiß: 
verschiedene Entwicklungsprogramme anstelle von Vstudio?) will ich nicht 
verwenden, da ich das später auf ein WinCE portieren will und hierzu den 
Platformbuilder benötige! Ich weiß, es kann sein, dass WinCE einige 
Bibliotheken evtl nicht unterstützt. Das interessiert mich vorerst aber 
nicht. Die Gesamtfunktionalität soll erstmals gegeben und umgesetzt 
sein.

Grüße, jowi

von Matthias H. (experimentator)


Lesenswert?

Hallo Jowi,

zu Deinem Posting von 13:47 und dem Problem des Crashs in Methoden:

Mir ist da gerade eine ganz dumme Idee gekommen: Könnte es sein, daß Du 
vergißt, das Objekt, auf das Du zugreifst, zu initialisieren 
(new-Operator bzw. statischer Konstruktoraufruf) oder Member-Variablen 
im Konstruktor zu initialisieren?

Tschüß, Matthias

von Vlad T. (vlad_tepesch)


Lesenswert?

also bei dem code muss es krachen, da dein readBuf NULL ist.

ansonsten geht der Code.
zeig doch mal bitte alles (Klassendefinition, Konstruktor). 
möglicherweise zerstört irgendwas vor dem split-Funktionsaufruf den Heap

von jowi (Gast)


Lesenswert?

Oh entschuldige, beim Testen war readBuf mit dem auskommentierten Text 
initialisiert.

Muss man eigentlich alle Klassenvariablen und Pointer im Konstruktor 
initialisieren? Also point = NULL, ints = 0?

Eigentlich solange ich nicht auf die durch Pointer verwiesenen 
Speicherstellen zugreife, sollte ja nichts passieren. Oder?

Ich initialisiere sie trotzdem mal vollständig.

Grüße, jowi

von jowi (Gast)


Lesenswert?

Im Konstruktor funktioniert malloc und new übrigens!

Die Ausführung in der Methode bleibt hier stehen:
1
// onexit.c
2
3
#pragma warning(suppress: 22008) /* prefast is confused */
4
            onexitend = p + (onexitend - onexitbegin);
5
            onexitbegin = p;
6
            __onexitbegin = (_PVFV *)_encode_pointer(onexitbegin);
7
        }
8
9
        /*
10
         * Put the new entry into the table and update the end-of-table
11
         * pointer.
12
         */
13
         *(onexitend++) = (_PVFV)_encode_pointer(func);
14
  HIER:  __onexitend = (_PVFV *)_encode_pointer(onexitend);
15
16
        return func;
17
}

Grüße, Jowi

von Vlad T. (vlad_tepesch)


Lesenswert?

also ich gebs auf.
da du nicht mit einem kompletten Minimalbeispiel rausrückst ist dir 
nicht zu helfen.
jetzt kommst du auf einmal scheinbar völlig zusammenhangslos mit c-files 
und wüstestem Pointercode.

wenn du VS benutzt, kann man höchstwahrscheinlich von einer konsistenten 
Toolchain ausgehen. Der Fehler liegt also zu 99% in dem Code, den du uns 
nicht gezeigt hast.

von jowi (Gast)


Lesenswert?

Der Code im letzten Beispiel stammt nicht von mir, in dem endet das 
Debuggen.

Ich möchte den gesamten Code nicht offenbaren, da es sich hierbei um 
einen Teil meiner Bachelorarbeit in einer Firma handelt und ich bei 
dieser eine Geheimhaltungsverpflichtung unterschrieben habe. Das will 
und traue ich mich einfach nicht. Ich hoffe, ihr könnt das 
nachvollziehen.

Ja, ich hatte während dem Studium mal C++ aber im Schmalspurverfahren 
und nicht sonderlich tief. Der Begriff des Vektoren im Zusammenhang mit 
C++ war mir z.B. bis gestern unbekannt.

Kannst du mir Tips geben, wo ich noch schauen könnte?
Die Definition meiner Klasse scheint mir korrekt:
1
class name {
2
 private:
3
  // Variablen- und Pointer-Deklarationen sowie private Methoden
4
5
 public:
6
  // Deklaration von Konstruktor, Destruktor und sonstigen Methoden
7
  // unter anderem auch splitline()
8
};

Im Konstruktor habe ich alles initialisiert. Pointer = NULL; INTs = 0;

Ja, irgendwo muss es dennoch an meinem Code liegen, da stimme ich dir 
zu. Alles innerhalb der Main-Funktion programmiert, funktioniert ja. Nur 
die Speicherallokierung innerhalb der Methoden der Klasse geht schief.

Komisch ist auch, dass in manchen Methoden die Allokierung schonmal 
funktionierte. Ich habe aber nichts verstellt/geändert?!?

Ich räume meinen Code mal auf, alles was nicht rein muss, kommt weg. 
Vielleicht finde ich ja den Fehler.

Ich würde mich dennoch über Tips und Ideen freuen.

Vielen Dank dir/euch!

Grüße, jowi

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

jowi schrieb:
> Ich möchte den gesamten Code nicht offenbaren,

Dann reduziere den auf ein compilierbares Beispiel, das Dein Problem 
enthält.

Funktioniert das Beispiel, hat Deine Reduktion die Fehlerursache 
beseitigt - und Du hast einen Anhaltspunkt, wo das Problem tatsächlich 
liegt.

Mit Codefetzchen und Schnipselchen wirst Du hier aber auf keinen grünen 
Zweig kommen.

von jowi (Gast)


Lesenswert?

Ok. Ich werde ein Beispiel in einem neuen Projekt programmieren, welches 
den Fehler reproduziert und euch das hier einstellen. Es wäre nett, wenn 
ihr dann nochmal einen Blick darauf werfen könntet.

Bis dahin und vielen Dank an alle!

Grüße, jowi

von Macros are evil (Gast)


Lesenswert?

Verwendest Du in der Klassendefinition (cpp-Datei) evtl. DEBUG-Macros, 
die new und malloc umdefinieren? Dann solltest Du dort mal ansetzen...

von Matthias H. (experimentator)


Lesenswert?

>Muss man eigentlich alle Klassenvariablen und Pointer im Konstruktor
>initialisieren? Also point = NULL, ints = 0?
>
>Eigentlich solange ich nicht auf die durch Pointer verwiesenen
>Speicherstellen zugreife, sollte ja nichts passieren. Oder?
>
>Ich initialisiere sie trotzdem mal vollständig.

Erst einmal, Klassenvariablen sind Variablen, die nicht einer Instanz 
einer Klasse, sondern der Klasse selbst gehören und von allen Instanzen 
der Klasse geteilt werden (Deklaration mit static ...). Du meinst 
wahrscheinlich Instanz- oder Objektvariablen. Man sagt im 
objektorientierten Jargon auch Member-Variablen.

Es ist dringend zu empfehlen, immer alles vollständig zu initialisieren. 
Es kann zwar wirklich nichts passieren, solange Du nicht drauf 
zugreifst, aber das ist guter Stil. Machst Du es nicht, so schaffst Du 
künstlich eine mögliche spätere Fehlerquelle - und das können Fehler 
sein, die sehr schwer zu analysieren sind, z. B. funktioniert in 
Entwicklungsversion/im Debugger, aber nicht in Release-Version, 
funktioniert sporadisch nicht, funktioniert auf Plattform A, aber nicht 
auf Plattform B usw.

Du mußt sie übrigens nicht unbedingt im Body des Konstruktors 
initialisieren:
1
MeineKlasse() : point(NULL), ins(0) {}

tut's auch. Das sind Konstruktoraufrufe, in diesem Fall der 
Standard-Konstruktoren. Braucht ein Konstruktor Parameter, dann muß man 
es sogar so machen.

von jowi (Gast)


Lesenswert?

Hallo alle miteinander!

Nochmals vielen Dank für eure Hilfe! Ich hab mir vieles zu Herzen 
genommen und mit großer Freude gelesen. Tips sind immer gut, da kann man 
nur lernen. Ich verwende jetzt auch meist Referenzparameter anstelle von 
Pointern. Es ist wahr, damit lässt sichs meist einfacher arbeiten und 
der Code bleibt/wirkt übersichtlicher.  :-)

Da ihr meintet, dass der Code ansonsten funktionstüchtig aussieht, habe 
ich nochmal ein neues, leeres Projekt erstellt, alle Einstellungen in 
den Projekteigenschaften selber vorgenommen und meine einzelnen 
Codeblöcke übernommen und teilweise verschönert und siehe da, plötzlich 
funktioniert es.

Dafür ging zwar nochmal gut ein Abend und ein Morgen drauf, aber dafür 
funktioniert's. Ich kann euch jetzt leider nicht genau sagen, wo der 
Fehler lag. Entweder im Code oder in dem vorkonfigurierten Visual Studio 
Projekt.

Auf jeden Fall danke nochmals! Habt nen schönen Tag.

Grüße, Jowi

von Karl H. (kbuchegg)


Lesenswert?

jowi schrieb:

> funktioniert's. Ich kann euch jetzt leider nicht genau sagen, wo der
> Fehler lag. Entweder im Code oder in dem vorkonfigurierten Visual Studio
> Projekt.

Er lag mit ziemlicher Sicherheit in deinem Code.
Irgendwo hast du in den nicht gezeigten Programmteilen die 
Runtime-Umgebung zerschossen. Das könnte zb ein Array-Zugriff Out of 
Bounds gewesen sein (das ist der häufigste Fall).

von Matthias H. (experimentator)


Lesenswert?

Hallo Karl Heinz,

> Er lag mit ziemlicher Sicherheit in deinem Code.
> Irgendwo hast du in den nicht gezeigten Programmteilen die
> Runtime-Umgebung zerschossen. Das könnte zb ein Array-Zugriff Out of
> Bounds gewesen sein (das ist der häufigste Fall).

Das ist recht wahrscheinlich, ich halte es aber ebenfalls für möglich, 
daß es tatsächlich eine zerschossene Projektdatei war - das habe ich bei 
dem Compiler auch schon erlebt, Winzigweich eben ;-)) .

Tschüß, Matthias

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.