array übergeben
-
Jetzt würde ich aber auch gerne Mal diese Klammerungen verstehen bzw. den Hintergrund der Schreibweisen verstehen.
int * a[5];
Bindet jetzt [] oder * stärker und was bedeutet das? Und gehört * überhaupt zum a oder int? Eigentlich doch zum int, aber die int*-Schreibweise verwirrt ja wiederum, weil:
int* a, b;
a als int-Zeiger aber b als int deklariert. Dann ist das von C++ aber unlogisch, wenn * eigentlich zum int gehört, oder?
So, und (*a) macht ja nur Sinn, wenn [] sonst stärker binden würde, also ist:
int * a[5];
äquivalent zu
int * (a[5]);
(es sei denn, die Klammern erhalten hier noch irgend eine andere Sonderbedeutung)Aber a[5] ohne den Typen macht für mich auch erstmal wenig Sinn. Kann mir das jemand verständlich erklären? Oder ist das willkürlich?
-
funk(int* arr); int array[3]; funk(&array) // Warnung aber funktioniert funk(array) // funktioniert auch
-
@Eisflamme: Du hast es dir quasi schon selber erklärt, du bist nur bei einer Grundannahme falsch und deshalb verwirrt: Das * gehört zur Variable, nicht zum Typen! Ich dachte immer, das wäre Grundbildung bei Pointern, weil es eben genau die Erklärung ist, warum bei
int *a,b;
das b kein Pointer ist. Das Sternchen heißt, dass der Bezeichner als ein Pointer auf den angegebenen Typ anzusehen ist (wodurch der Bezeichner dann vom Typ "Pointer auf X" wird).Der Rest ist dann die Stärke der Bindung, [] bindet stärker als *.
-
blurry333 schrieb:
void funk(int* arr); int array[3]; funk(&array); // Warnung aber funktioniert funk(array); // funktioniert auch
Dein "Warnung aber funktioniert" kompiliert bei mir nicht.
Lieber blurry333. Dieses Beispiel ist nicht selbsterklärend. Es ist nicht klar, was du damit sagen wolltest. Es steht jedenfalls in keinem Widerspruch zu dem, was hier von mir, Wutz, DirkB und CStoll gesagt worden ist.
Weißt Du, was eine implizite Konvertierung ist? Das ist eine Konvertierung die automatisch stattfindet, ohne dass man dafür etwas besonderes hat hinschreiben müssen. Beispiel:
void foo(double); int main() { int i = 3; foo(i); }
Ist i jetzt auch ein double, weil Du foo mit i aufrufen kannst und foo einen double erwartet? Nein. i ist ein lvalue-Ausdruck vom Typ int. Das was foo bekommt ist ein double-Wert. Hier findet eine unsichtbare Konvertierung statt.
Genauso verhält es sich mit dem array-to-pointer decay. Arrays sind keine Zeiger. Aber Du kannst einen Array-Ausdruck vom Typ T[N] dort verwenden, wo ein T* erwartet wird. Das liegt an dieser impliziten Konvertierung. Das Ergebnis dieser Konvertierung ist die Adresse des ersten Array-Elements.
int arr[7]; int* p = arr; // implizite Konvertierung int* q = &arr[0]; // explizit die Adresse des ersten Elenents holen assert(p==q);
-
krümelkacker schrieb:
Lieber blurry333. Dieses Beispiel ist nicht selbsterklärend. Es ist nicht klar, was du damit sagen wolltest. Es steht jedenfalls in keinem Widerspruch zu dem, was hier von mir, Wutz, DirkB und CStoll gesagt worden ist.
Lieber Vorredner, bitte hier nicht zuviel Energie vergeuden:
-
Wutz schrieb:
Danke für den Hinweis. Das Maß war eh voll jetzt. Wurde ja auch schon alles gesagt.
-
Soweit ich das verstanden habe, bist du, blurry333, mittlerweile berühmt dafür, relativ inhaltsleere Posts abzugeben, nicht wahr?
Aber gut, ich will mal nicht so sein ...
Ein kurzes Beispiel zu eindimensionalen Arrays:
#include <iostream> //Beides ist möglich, werden aber immer als Zeiger angesehen: int foo(int*p1,int p2[],int plen1,int plen2) { //Elementweise ausgeben. for(int i=0;i<plen1;i++) std::cout<<p1[ i]<<" "; std::cout<<"\n"; //Und auch hier. Zugriff ist derselbe, für den Compiler handelt es sich bei //p1 und p2 nur um Zeiger, Arrays werden nicht kopiert, höchstens die //Zugriffsadresse - wie beim Zeiger. for(int i=0;i<plen2;i++) std::cout<<p2[ i]<<" "; std::cout<<"\n"; } int main() { //Ein Array auf dem Heap ... int*a=new int[10]; for(int i=0;i<10;i++) a[ i]=i; //... und auf dem Stack. int b[10]={0,1,2,3,4,5,6,7,8,9}; foo(a,b,10,10); //Ach ja, Zeiger löschen nicht vergessen. delete[] a; }
Bei Strings ist das ganze ein bisschen komplizierter, diese gibt es nativ nur mit Arrays von
char
:int main(int ArgC,char**ArgV) { for(int i=0;i<ArgC;i++) { //KOPIERT NICHT, sondern setzt den Wert und damit den Zugriff von //CurrentString auf das aktuelle Element von ArgV. char*CurrentString=ArgV[ i]; //Gibt den String direkt aus std::cout<<"Gesamt: "<<CurrentString<<"\n"; //Gibt den String Buchstaben für Buchstaben aus std::cout<<"Einzeln: "; for(int j=0;CurrentString[j];j++) std::cout<<CurrentString[j]; std::cout<<"\n"; } }
Der Arrayname ist eigentlich nur eine Adresse, die übergeben wird. Wenn du den Adressoperator davorsetzt, wird der Compiler warnen, dass du die Adresse des Arrays explizit übergeben willst, aber es wird funktionieren. Bei Zeigern darfst du das allerdings nicht machen, es sei denn, du arbeitest mit Zeigern auf Zeigern und dereferenzierst diese, denn damit würdest du nicht die Adresse im Zeiger, sondern die Adresse des Zeigers übergeben, und die liegt oft auf dem Stack - es sei denn mal wieder, du arbeitest mit Zeigern auf Zeigern und Zeigerarrays, die auf dem Heap liegen.
Lange Rede, gar kein Sinn: oben steht, wie's gemacht wird.
@all: Lasst ihn doch, meine Güte. Er hat Fragen, also stellt er diese. Ist ja nicht so, als ob man euch dazu zwingen würde, zu posten, also tut es auch nicht.
-
ok danke für Eure Beiträge.
-
so leider noch eine Frage.
Wenn man an eine funktion ein mehrdimensionales array übergibt muss die größe
der 2.ten - nten dimension feststehen.z.B.
funktion(arr[][4],int arraysize)
Muss ich jetzt für jedes array einer anderen Größe eine neue Funktion schreiben ?
-
blurry333 schrieb:
Muss ich jetzt für jedes array einer anderen Größe eine neue Funktion schreiben ?
Wow, eine ausformulierte Frage.
. Da antworte ich sogar mal:
Nein. Übergib dir die Größen und einen Pointer auf das erste Element und rechne in der Funktion die Indizes selber um (z.B. 2D: Index [x][y] entspricht (Anfang + x * y-Größe + y) . Oder lass mehrdimensionale Arrays gleich bleiben, es gibt in C++ keinen Grund, sich so zu quälen.
-
In C wäre es üblich, die Indizierung zur Laufzeit von Hand zu machen, d.h.
void foo(int *array, size_t dim_y, size_t dim_x) { size_t i, j; array[i * dim_y + j]; // entspricht array[i][j]. }
Aber wir sind hier ja zum Glück im C++-Forum, wo man Operatoren überladen und TMP betreiben kann:
#include <cstddef> #include <iostream> template<typename T> class array2d { public: template<std::size_t M, std::size_t N> inline array2d(T (&data)[M][N]) : data_(static_cast<T*>(static_cast<void*>(data))), dim_x_(N), dim_y_(M) { } inline T *operator[](std::size_t y) { return data_ + dim_x_ * y; } inline T const *operator[](std::size_t y) const { return data_ + dim_x_ * y; } inline std::size_t dim_x() const { return dim_x_; } inline std::size_t dim_y() const { return dim_y_; } private: T *data_; std::size_t dim_x_; std::size_t dim_y_; }; void funktion(array2d<int> const &arr) { for(std::size_t i = 0; i < arr.dim_y(); ++i) { for(std::size_t j = 0; j < arr.dim_x(); ++j) { std::cout << arr[i][j] << ' '; } std::cout << '\n'; } } int main() { int a[][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; funktion(a); }
Das macht im Prinzip das selbe, nur halt schick verpackt.
-
Memberfunktionen sind implizit inline
-
seldon schrieb:
In C wäre es üblich, die Indizierung zur Laufzeit von Hand zu machen, d.h.
void foo(int *array, size_t dim_y, size_t dim_x) { size_t i, j; array[i * dim_y + j]; // entspricht array[i][j]. }
// Entspricht undefiniertem Verhalten.
-
314159265358979 schrieb:
Memberfunktionen sind implizit inline
Ihm ging es eh nur darum den Code aufzublaehen und bissi zum zu posen.
-
Ich frage mich ja, ob nicht das hier UB ist:
static_cast<T*>(static_cast<void*>(data))
-
das dim_y in
array[i * dim_y + j];
muss natürlich dim_x heißen, mein Fehler.
Was
static_cast<T*>(static_cast<void*>(data))
angeht, das ist sehr genau definiert. Der Umweg über void* ist aufgrund des strengen Typsystems in C++ notwendig. In der Praxis dürfte
reinterpret_cast<T*>(data)
wohl das selbe machen, aber die Definition von reinterpret_cast ist deutlich weniger streng.
-
seldon schrieb:
das dim_y in
array[i * dim_y + j];
muss natürlich dim_x heißen, mein Fehler.
Asoooo, dann:
// seldons Code void foo(int *array, size_t dim_y, size_t dim_x) { size_t i, j; // enspricht undefiniertem Verhalten. array[i * dim_x + j]; // entspricht array[i][j]. }
-
Dann füg noch /* i, j initialisieren */ ein. Meine Güte, ich wollte damit ein Prinzip verdeutlichen, nicht einsatzbereiten Code zur Verfügung stellen.
-
ubler schrieb:
seldon schrieb:
das dim_y in
array[i * dim_y + j];
muss natürlich dim_x heißen, mein Fehler.
Asoooo, dann:
// seldons Code void foo(int *array, size_t dim_y, size_t dim_x) { size_t i, j; // enspricht undefiniertem Verhalten. array[i * dim_x + j]; // entspricht array[i][j]. }
Bist du so blöd oder stellst du dich nur so?
-
seldon schrieb:
: data_(static_cast<T*>(static_cast<void*>(data))), dim_x_(N), dim_y_(M) { }
Das geht auch einfacher und auch so, dass T = const Irgendwas unterstützt wird:
: data_(data[0]), ...
seldon schrieb:
inline T *operator[](std::size_t y) { return data_ + dim_x_ * y; } inline T const *operator[](std::size_t y) const { return data_ + dim_x_ * y; }
Die Klasse hat Referenzsemantik. Da ist diese const-Überladung unangebracht. Der Zugriff auf das Array verändert die "Referenz" ja nicht. Das inline ist auch überflüssig. Von daher würde ich schreiben:
T *operator[](std::size_t y) const { return data_ + dim_x_ * y; }
Den Konstruktor kann man auch noch etwas flexibler machen:
template<class U, std::size_t M, std::size_t N> array2d(U (&data)[M][N], typename boost::enable_if_c< boost::is_same<T,U>::value || boost::is_same<T,const U>::value >::type* =0) : data_(data[0]), dim_x_(N), dim_y_(M) {}
Dann klappt das auch mit dem const richtig:
void funktion(array2d<[b]const[/b] int> arrayref) { for(std::size_t i = 0; i < arrayref.dim_y(); ++i) { for(std::size_t j = 0; j < arrayref.dim_x(); ++j) { std::cout << arrayref[i][j] << ' '; } std::cout << '\n'; } }
Jetzt fehlt eigentlich nur noch eine implizite Konvertierung array2d<Dings> nach array2d<const Dings> in der Klasse. Man darf natürlich keine Derived->Base Konvertierung zulassen. Deswegen habe ich oben auch nicht convertible<U*,T*>::value verwendet.
mein Senf,
kk