Zeigergröße beibehalten
-
Folgendes Problem:
ich habe in meiner main-Funktion einen char-Zeiger der 2 Bytes groß ist.
char test[2]Gleichzeitig habe ich eine Funktion die einen char-Zeiger entgegennimmt.
int testFunc(char *buffer);In dieser Funktion wird mittels eines sizeof() die Größe der Variablen buffer bestimmt. Wenn ich jetzt meinen 2 Bytes-char an diese Funktion übergebe habe ich am Ende einen 4-Bytes-Zeiger statt meinen 2-Bytes.
Wie kann ich das Problem lösen?
-
Das
char test[2]ergibt ein char-Array, das zwei Elemente besitzt.
Da sizeof(char) per Definition eins ist, ist das Array zwei Byte groß.In der Funktion ist buffer ien Zeiger. und die sind auf deinem System wohl 32 Bit groß.
Wenn du jetzt
testFunc(test)machst, verweist buffer auf dein Array.
Also wo ist das Problem?
-
Celebrariel schrieb:
Folgendes Problem:
ich habe in meiner main-Funktion einen char-Zeiger der 2 Bytes groß ist.
char test[2]Nein, das ist ein Array aus 2 chars, das hat mit Zeigern gar nichts zu tun, außer dass es kontextabhängig in einen Zeiger auf sein erstes Element umgewandelt werden kann. Der wäre dann vom Typ eines Zeigers auf char.
Gleichzeitig habe ich eine Funktion die einen char-Zeiger entgegennimmt.
int testFunc(char *buffer);In dieser Funktion wird mittels eines sizeof() die Größe der Variablen buffer bestimmt. Wenn ich jetzt meinen 2 Bytes-char an diese Funktion übergebe habe ich am Ende einen 4-Bytes-Zeiger statt meinen 2-Bytes.
Das liegt da dran, dass du die Größe des Zeigers bestimmst, nicht die Größe des Objektes, auf die gezeigt wird. Die Größe eines Zeigertypen ist bei dir anscheinend 4, was durchaus üblich ist.
Wie kann ich das Problem lösen?
Gar nicht. Aber es gibt in jeder Hinsicht bessere Lösungen:
-keine rohen Arrays benutzen, benutz std::array (oder std::tr1::array bei älteren Compilern). Oder die anderen Container der Standardbibliothek. Die Verhalten sich alle ganz intuitiv bei Funktionsaufrufen und haben keine verwirrenden Sonderregeln.
-Anfang und Ende einer Sequenz als Funktionsparameter benutzen, nicht die ganze Sequenz. Vorbild nach Art der algorithms aus der STL.
-Bei den obigen beiden Tipps sollte man auch noch Templates benutzen, um sich nicht an einen bestimmten Typ von Container zu binden. Ist für Anfänger vielleicht ein bisschen viel, aber merk dir das mal vor. Vorbild wieder die STL.
-Vielleicht brauchst du das alles gar nicht. Falls dein char-Array eine Zeichenkette darstellen soll, ist Zeichenkettenbehandlung in der Standardbibliothek bereits gelöst. Falls die Funktion etwas halbwegs einfaches macht, dann wird es dafür vielleicht schon einen fertigen algorithm geben. Müsste man wissen, um was es genau geht, um dich beraten zu können.
-
Indem du die Grösse explizit mitgibst.
char test[2]ist auch kein Zeiger, sondern ein Array.
Das sind grundverschiedene Dinge.
Ein Array *besteht* aus mehreren Objekten (in deinem Fall chars).
Ein Zeiger *zeigt* auf ein solches Objekt. C++ erlaubt es dir lediglich den Namen eines Arrays wo hinzuschreiben, wo ein Zeiger gebraucht wird. In dem Fall wird ein Zeiger auf das erste Element des Arrays erzeugt. Die Information wie gross das Array war (bzw. ist, es existiert ja weiterhin) geht dabei allerdings verloren (heisst: kann nicht über den Zeiger abgerufen werden - sizeof(Array) funktioniert natürlich weiterhin).
-
Celebrariel schrieb:
In dieser Funktion wird mittels eines sizeof() die Größe der Variablen buffer bestimmt. Wenn ich jetzt meinen 2 Bytes-char an diese Funktion übergebe habe ich am Ende einen 4-Bytes-Zeiger statt meinen 2-Bytes.
Wie kann ich das Problem lösen?#include <iostream> void option_1(char* buffer, unsigned size) { std::cout << '\n' << "size = " << size; } void option_2(char (&buffer)[2]) { std::cout << '\n' << "size = " << sizeof(buffer); } template<typename T, unsigned size> void option_3(T (&buffer)[size]) { std::cout << '\n' << "size = " << size; } int main() { char test[2]; option_1(test,sizeof(test)); option_2(test); option_3(test); }Eine viel bessere Lösung ist aber:
#include <iostream> #include <array> #include <vector> void cpp_11(std::array<char,2>& buffer) // Falls du C++11 hast, ... { std::cout << '\n' << "size = " << buffer.size(); } void cpp(std::vector<char>& buffer) // ... ansonsten so. { std::cout << '\n' << "size = " << buffer.size(); } int main() { std::array<char,2> test = { '0', '1' }; cpp_11(test); std::vector<char> test_2; test_2.push_back('0'); test_2.push_back('1'); cpp(test_2); }
-
@Gugelmoser:
Ich verstehe nicht was an deiner viel besseren Lösung viel besser sein soll. Bzw. überhaupt besser.Ich würde sagen die Zeiger + Grösse Lösung (alternativ: Zeiger + Zeiger) ist oft die bessere, oft sogar optimal.
Wenn es Sinn macht kann man Wrapper-Funktionen wie das von dir gezeigte Template machen, vielleicht auch Wrapper für std::string, std::vector<char> etc.Manchmal mag es Sinn machen statt einer Funktion ein Funktionstemplate zu machen, welches dann Iteratoren statt Zeigern akzeptiert. Oft ist aber auch schon das Overkill.
Dem Aufrufer allerdings einen Container aufzuzwingen, nur weil ... ja warum eigentlich?, halte ich für keine gute Idee. Das führt bloss zu Code wo man unnötigerweise den Heap bemüht und mächtig viel Speicher rumkopiert = langsam = finde ich nicht so toll.
-
hustbaer schrieb:
Dem Aufrufer allerdings einen Container aufzuzwingen, nur weil ... ja warum eigentlich?, halte ich für keine gute Idee. Das führt bloss zu Code wo man unnötigerweise den Heap bemüht und mächtig viel Speicher rumkopiert = langsam = finde ich nicht so toll.
Ich habe nochmals darüber nachgedacht. Du hast Recht, ein
std::vectorist doch nicht die bessere Lösung. Einstd::arrayfinde ich aber sehr wohl die bessere Lösung. Wie oft kommt es bei Anfängern vor, dass sie einen Zugriff außerhalb der Arraygrenzen machen. Ich finderange-checks in debug-buildsvon großem Vorteil. Auch können sich wohl viele Anfänger nicht mit demarray to pointer decayanfreunden. In dieser Hinsicht bietetstd::arraymit seiner Kopiersemantik einen weiteren Vorteil gegenüber rohen Arrays.
-
Gugelmoser schrieb:
hustbaer schrieb:
Dem Aufrufer allerdings einen Container aufzuzwingen, nur weil ... ja warum eigentlich?, halte ich für keine gute Idee. Das führt bloss zu Code wo man unnötigerweise den Heap bemüht und mächtig viel Speicher rumkopiert = langsam = finde ich nicht so toll.
Ich habe nochmals darüber nachgedacht. Du hast Recht, ein
std::vectorist doch nicht die bessere Lösung. Einstd::arrayfinde ich aber sehr wohl die bessere Lösung. Wie oft kommt es bei Anfängern vor, dass sie einen Zugriff außerhalb der Arraygrenzen machen. Ich finderange-checks in debug-buildsvon großem Vorteil. Auch können sich wohl viele Anfänger nicht mit demarray to pointer decayanfreunden. In dieser Hinsicht bietetstd::arraymit seiner Kopiersemantik einen weiteren Vorteil gegenüber rohen Arrays.Es geht nicht darum, dass die Wahl von std::array an sich schlecht wäre, sondern dass ein Interface, welches nur std::array, noch dazu von einer festen Größe, sehr einschränkend ist. In C++ hat man ja nicht nur Arrays, sondern viele verschiedene Datencontainer zur Verfügung. In C hat man es eben einfacher, da es da praktisch nur Arrays gibt. Das macht es natürlich für Anfänger wesentlich komplizierter, Datensammlungen an Funktionen zu übergeben, denn richtig glänzen tut C++ hier erst mit Templates, die dann schon ein etwas gehobeneres Thema sind.
-
Celebrariel schrieb:
In dieser Funktion wird mittels eines sizeof() die Größe der Variablen buffer bestimmt.
Das ist der Anfängerfehler.
Ein direkt an eine Funktion übergebenes Array wird immer implizit in einen Zeiger gewandelt, und du lässt sizeof auf den Zeiger los und nicht auf das Array und erhälst somit die Größe des Zeigers und nicht die Größe der Daten, auf die der Zeiger verweist.
Dabei ist es egal, was du bei der Deklaration angibst:int testFunc(char *buffer); int testFunc(char buffer[]); int testFunc(char buffer[2]); int testFunc(char buffer[4711]);Immer wird ein sizeof das sizeof(char*) liefern.
In C musst du eine Arraygrößeninformation als zusätzlichen Paramter angeben, entweder die Gesamtgröße in Bytes oder die Anzahl der Elemente des Arrays (wegen sizeof(char)==1 wäre bei dir beides gleich).
Oder du übergibst einen Zeiger auf das Array, dann musst du den Arraytyp aber explizit angeben und du gewinnst prinzipiell nicht sehr viel:int testFunc(char (*buffer)[2]) /* Zeiger auf ein char[2] Array */ { size_t arraygrosse = sizeof(*buffer); ... } char a[2]; testFunc(&a);