PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++: Pointer auf Array


Marscel
2006-09-16, 17:31:41
Mein Programm: Daten aus einer Datei auslesen, splitten, in Integer casten und in eine Membervariable einer Klasse abspeichern. Ich habe eine Klasse, in der irgendwo das hier steht:
private:
int * products[];

Das soll ein Pointer auf ein int-Array sein, richtig? Pointer deswegen, weil noch nicht abzusehen ist, wie groß das Array sein wird.

Folgend geht es weiter, diese Funktion übernimmt einen Vektor von string und einen Pointer auf ein Array. Ist letzteres richtig?
//Prototyp
void strings_to_integer(std::vector<std::string> &str_vector, int* ints[]) const;

Es wird in einer Memberfunktion das übergeben, der Vektor kommt so rüber, wie er soll, "products", die Membervariable von oben, weiß ich nicht...
strings_o_integer(str_vector, products);

In der Funktion passiert das:
int temp_int_array[str_vector.size()];

for(int i = 0; i < str_vector.size(); ++i)
{
temp_int_array[i] = atoi(str_vector.at(i).c_str());
}
... // fehlend hier, Adresse überschreiben, über ints alle Werte noch einmal ausgeben


So, und was muss ich jetzt machen, dass ich z.B. die Adresse von "products" mit der von "temp_int_array" überschreibe? Dann würde products auf ein int-Array zeigen, undzwar das, was ich hier erstelle, richtig?

Ich hatte folgendes versucht und dutzend weitere Möglichkeiten:
*ints = temp_int_array;

Aber das will dann später beim Abrufen mittels ints[i] nicht, und wenn ich unter der Adresse mit **(ints+i) (i ist natürlich ein int) nachschaue, kommen entweder Zahlen raus, die ich nicht brauche oder Speicherzugriffsfehler.

Könnt ihr mir helfen? Als PHP-Skript-Hersteller kennt man nur Referenzen und Arrays, die sich nicht auch wie Pointer oder so behandeln lassen. ;)

Gnafoo
2006-09-16, 18:39:35
So, und was muss ich jetzt machen, dass ich z.B. die Adresse von "products" mit der von "temp_int_array" überschreibe? Dann würde products auf ein int-Array zeigen, undzwar das, was ich hier erstelle, richtig?

Ich hatte folgendes versucht und dutzend weitere Möglichkeiten:
*ints = temp_int_array;

Aber das will dann später beim Abrufen mittels ints[i] nicht, und wenn ich unter der Adresse mit **(ints+i) (i ist natürlich ein int) nachschaue, kommen entweder Zahlen raus, die ich nicht brauche oder Speicherzugriffsfehler.

Könnt ihr mir helfen? Als PHP-Skript-Hersteller kennt man nur Referenzen und Arrays, die sich nicht auch wie Pointer oder so behandeln lassen. ;)

Das Problem ist, dass dein Array beim Verlassen der Funktion ungültig wird, da es nicht explizit alloziiert wurde. Du solltest stattdessen folgendes machen:

(*ints) = new int[str_vector.size()];

for(int i = 0; i < str_vector.size(); ++i)
{
(*ints)[i] = atoi(str_vector.at(i).c_str());
}


du solltest allerdings nicht vergessen das Array "products" wieder mit "delete[] products;" zu löschen, wenn es nicht mehr benötigt wird.

Desweiteren ist "int products[];" bereits ein Pointer (durch das [], ein Array ist im Prinzip nur ein Pointer auf das erste Element eines Arrays und der Zugriff geschieht durch Pointer-Arithmetik). D. h. in der Klasse reicht es ohne "*". Beim Übergeben an die Funktion musst du aber afaik einen Pointer der Variable in der Klasse übergeben, welche auf das Array zeigt. Also "&products". Der Typ der Variable in der Funktion sollte also stimmen.

Ich hoffe mal ich habe jetzt nichts durcheinander gebracht. Aber nebenbei gefragt, warum benutzt du für "products" nicht einfach einen vector? Aus Platzgründen?

Edit: Möglicherweise muss es in deiner Funktionsdeklaration auch
void strings_to_integer(std::vector<std::string> &str_vector, int (*ints)[]) const;
heißen (zur Unterscheidung zwischen "Pointer auf Array" und "Array von Pointern") da bin ich mir nicht ganz sicher.

Neomi
2006-09-16, 18:40:20
int * products[];

Das ist kein Pointer auf ein Array von ints, sondern ein Array von int*. Ein dynamisches Array wäre in deinem Fall einfach ein "int * products;".

Beim Übergeben an die Funktion musst du aber afaik einen Pointer der Variable in der Klasse übergeben, welche auf das Array zeigt. Also "&products". Der Typ der Variable in der Funktion sollte also stimmen.

Das Array ist doch schon der Pointer. Dieser Pointer selbst hat keine Adresse, weil er berechnet wird (in dem Fall als Offset zum this-Pointer). Also entweder "products" oder "&products[0]", aber nicht "&products".

Gnafoo
2006-09-16, 18:48:11
Er will doch in der Funktion die Variable (den Pointer) auf das Array ändern (denn das Array wird in der Funktion alloziiert, da die Größe zuvor unbekannt ist. Also steht der Wert des Pointers erst in der Funktion fest). Deshalb muss er den Pointer selber entweder als Referenz oder als Pointer an die Funktion übergeben.

Alternativ kann man natürlich auch einfach die Funktion einen Pointer auf das Array zurückliefern lassen. Wäre das Array statisch hättest du natürlich recht. Da würde es anders auch ausreichen.

Neomi
2006-09-16, 19:50:58
Wahrscheinlich zu schnell gelesen. Ja, in dem Fall stimmt das mit dem &products natürlich. Ich hatte da noch das Array (unbepointert) im Kopf.

Marscel
2006-09-17, 01:45:54
Cool, danke. Eure Tipps funktionieren.

Aber damit ich nochmal nachvollziehe, was ich tue:

strings_to_integer(std::vector<std::string> &str_vector, int (*ints[])) const

int (*ints[]) bedeutet int-Array-Pointer?

(*ints) = new int[str_vector.size()];

Alloziert Speicher für ein int-Array, dessen Adresse auf dem ints-Pointer landet?
Dass man Pointer initialisieren kann, ist klar, aber ich ging davon aus, die Zuweisung einer Adresse auf den Pointer ginge auch, was aber durch *ints nicht gegeben ist, sondern durch (*ints), richtig?

Aber nebenbei gefragt, warum benutzt du für "products" nicht einfach einen vector? Aus Platzgründen?

Weil eigentlich nichts weiter nötig ist, als einmal alle Werte rein da und gut, im Programmablauf werden die sich nicht mehr ändern.

Marscel
2006-09-17, 02:41:05
Viel wichtiger, wie ermittel ich die gesamte Speicherbelegung nun?

sizeof((*ints)) gibt nur 4 zurück (Pointer-Größe nehm ich mal).

Wenn das nicht geht, spring ich doch auf Vektoren um.

Trap
2006-09-17, 10:40:13
Nimm std::vector, der ist auch für solche Anwendungen gut geeignet.

Ansonsten ist dein Code was Arrays und Pointer angeht ziemlich konfus. [] und * bedeutet in vielen Fällen das gleiche und beides zu benutzen ist doppelt gemoppelt. C++ hat keine Arrays mit dynamischer Größe, überhaupt sind die eingebauten Arrays nur für scoped arrays mit fester, kleiner Größe sinnvoll. Bei allem anderen würde ich std::vector nehmen.

Ich versuche soweit wie möglich nackte Pointer zu vermeiden, damit hat man immer Ärger, selbst wenn man alle x Regeln, was man nicht machen darf, im Kopf hat. Wan übersieht doch hin und wieder mal etwas und dann darf man Pointerprobleme debuggen was meist nicht lustig ist.

Achso:
cdecl> explain int (*ints[])
declare ints as array of pointer to int
Wobei es an der Stelle vielleicht auch als Funktionspointer angesehen wird, die Syntax von Funktionspointern hab ich nie wirklich auswendig gelernt, das was du geschrieben hast sieht allerdings so ähnlich aus.

Marscel
2006-09-17, 13:34:33
Gut, mit std::vector geht das alles ein wenig reibungsloser. Kurze Frage noch, wenn ich eine Elementfunktion als const markiere, sprich dass diese eigentlich nichts am Objekt verändert, man mittels Referenz aber auch Klassenelemente übergeben kann, die verändert werden und somit auch das Objekt verändert wird: Darf man das? Oder das const da besser doch weglassen?

Gnafoo
2006-09-17, 13:59:00
Cool, danke. Eure Tipps funktionieren.

Aber damit ich nochmal nachvollziehe, was ich tue:

strings_to_integer(std::vector<std::string> &str_vector, int (*ints[])) const

int (*ints[]) bedeutet int-Array-Pointer?

Ich meinte eigentlich (*ints)[] für die Funktionsdeklaration und hab das aber auch nur durch suchen gefunden (weil ich sowas normal nie brauche). Deshalb hab ich auch geschrieben, dass ich mir nicht ganz sicher bin :D. Wenn möglich würde ich, wie Trap schon meinte versuchen so etwas zu vermeiden (und nen vector o. ä. zu nehmen. Mit Pointern passieren leicht Fehler, die Speicherlecks o. ä. verursachen). **ints wäre aber soweit ich weiß auch möglich und evtl. eindeutiger (oder eine Referenz auf den Pointer zum Array &ints[] iirc. Eigentlich wäre das wohl noch besser. Aber dann musst du in der Funktion auch "ints" statt "*ints" benutzen).


(*ints) = new int[str_vector.size()];

Alloziert Speicher für ein int-Array, dessen Adresse auf dem ints-Pointer landet?

Genau. Der Pointer *ints ist dabei genau die Variable, die auch in deiner Klasse verwendet wird.


Dass man Pointer initialisieren kann, ist klar, aber ich ging davon aus, die Zuweisung einer Adresse auf den Pointer ginge auch, was aber durch *ints nicht gegeben ist, sondern durch (*ints), richtig?

Ob *ints oder (*ints) sollte hier (im Gegensatz zur Parameterdeklaration) eigentlich keinen Unterschied machen. Ich habe die Klammern nur dazugeschrieben, weil ich mir nicht sicher war, ob sich C++ daran stört, wenn ich keine habe.

[Anm: Auf die Frage, wieso keinen Vector?] Weil eigentlich nichts weiter nötig ist, als einmal alle Werte rein da und gut, im Programmablauf werden die sich nicht mehr ändern.
In dem Falle würde ich trotzdem lieber einen Vector nehmen. Das erspart einfach eine Menge Ärger und du vermeidest potentielle Fehler. Wenn es performancekritisch wäre ... ok dann vielleicht ein Array, aber sonst tut es imho der Vector genauso gut.

Viel wichtiger, wie ermittel ich die gesamte Speicherbelegung nun?

sizeof((*ints)) gibt nur 4 zurück (Pointer-Größe nehm ich mal).

Wenn das nicht geht, spring ich doch auf Vektoren um.


Also noch mal zum Rekapitulieren: in deiner Funktion ist "ints" ein Pointer auf den Pointer, der auf das Array zeigt. "(*ints)" ist der Pointer der auf das Array zeigt. "(**ints)" ist das erste Element im Array. D. h. "sizeof(*ints)" gibt die Größe des Pointers zurück. Und die ist logischerweise 4. Die Größe des Arrays müsstest du glaube ich sogar extra abspeichern. Iirc konnte man die nicht einfach so nachträglich abfragen.

Kurze Frage noch, wenn ich eine Elementfunktion als const markiere, sprich dass diese eigentlich nichts am Objekt verändert, man mittels Referenz aber auch Klassenelemente übergeben kann, die verändert werden und somit auch das Objekt verändert wird: Darf man das? Oder das const da besser doch weglassen?
Man kann es glaube ich machen. Aber dann ist const doch sehr irreführend. Zumal du das Objekt (sollte es als const deklariert sein) nicht an deine Funktion übergeben kannst, weil diese nur einen nicht-const-Paramenter hat. Ok mit ein bisschen hässlichen Casten bekommt man das im Prinzip hin, aber wozu?
Dann lieber die Funktion als nicht-const deklarieren und direkt auf die Klassenelemente zugreifen.

Marscel
2006-09-17, 17:51:01
Vielen Dank, nützliche Tipps. :)