array übergeben
-
CStoll schrieb:
Kóyaánasqatsi schrieb:
Ich weiß nicht genau was du meinst.
Und hast du mal versucht, in der Funktion etwas mit dem übergebenen Array zu machen.
Auf die schnelle:
#include <iostream> void test(int sizeA, int sizeB, int **b) { for(int n = 0; n < sizeB; ++n) { for(int i = 0; i < sizeA; ++i) { b[i][n]; } } } int main() { int a[5][7]; test(5, 7, (int**)a); test; return 0; }
-
Ich rede nicht von irgendwelchen noop-Spielereien, die jeder Amateur-Compiler wegoptimieren kann, sondern von echten Zugriffen - und da macht sich der Cast nicht besonders gut.
PS: Mit den richtigen Parametern sieht es schon wesentlich besser aus.
-
#include <iostream> #include <array> template<std::size_t Size1, std::size_t Size2> void funk(std::array<std::array<int, Size1>, Size2>& p) { cout<<"Funktioniert"; } int main() { std::array<std::array<int, 4>, 3> arr; funk(arr); }
Fixed
-
Kóyaánasqatsi schrieb:
void test(int sizeA, int sizeB, int **b); int main() { int a[5][7]; test(5, 7, (int**)a); return 0; }
Lieber Kóyaánasqatsi, das, was Du da von Dir gegeben hast, lässt sich zwar wegen des Casts kompilieren, ist aber völlig unbrauchbar. Der Cast ist äquivalent zu einem reinterpret_cast von int(*)[7] zu int**. Es hat einen Grund, warum Dich der Compiler diese Konvertierung nicht ohne einen reinterpret_cast durchführen lässt. Ein Zeiger auf ein 7-elementiges int-Array ist eben kein Zeiger auf einen int-Zeiger.
-
ein mehrdimensionales array von char wird aber doch auch mit einem char** übergeben. Siehe nur argv .
-
blurry333 schrieb:
ein mehrdimensionales array von char wird aber doch auch mit einem char** übergeben. Siehe nur argv .
Nein.
argv ist ein Array von char*
Bei einem mehrdimensionales Array liegen alle Elemente hintereinander im Speicher, wobei der ganze rechte Index am schnellsten wechselt.
char a[3][4]
liegt im Speicher als a00, a01, a02 a03, a10, a11, a12, a13, a20, a21, a22 ,a23.Damit entspricht a einem
char*
Wenn du ein
char a[z][s]
hast, kannst du auf das Elementa[i][j]
auch mit*(a+i*s+j)
zugreifen
-
argv[0] argv[1] argv[2] . . usw
werden mit char** argv übergeben
-
blurry333 schrieb:
ein mehrdimensionales array von char wird aber doch auch mit einem char** übergeben. Siehe nur argv.
Das kommt auf Deine Definition von "mehrdimensionales Array" an. Ich verwende diesen Begriff erst gar nicht. Eindeutiger ist da schon "Array von Arrays" versus "Array von Zeigern".
int w[5][7]; // 5-elementiges Array von 7-elementigen Arrays int (*x)[7] = w; // Zeiger auf 7-elementiges Array (zeigt auf das erste Element von w) int *y[5]; // 5-elementiges Array von Zeigern int **z = y; // Zeiger auf Zeiger (zeigt auf das erste Element von y)
Per array-to-pointer decay kommt man von w nach x und von y nach z. Und da Zeiger keine Arrays sind, macht
int **silly = (int**)w;
absolut keinen Sinn.
Das einzige, was w,x,y,z, gemeinsam haben ist, dass man zweimal den []-Operator drauf anwenden kann. Wenn das schon ausreicht, um von "zweidimensionalen Arrays" zu sprechen, dann bist Du leider unpräzise, blurry333; denn man weiß dann nicht wirklich, was für ein Speicherlayout Du Dir vorstellst.
Irgendwas noch unklar? Dann guckt doch am besten mal in einem schlauen Buch nach, statt Fehlinformationen aus bescheidenen Tutorials zu extrahieren oder sie selbst zu produzieren.
-
CStoll schrieb:
@blurry: Damit die Funktion korrekt arbeiten kann, muß sie einen Zeiger auf int-Arrays bekommen:
void func(int* arr[3]);
Das ist kein Zeiger auf int-Arrays sondern ein Array von Zeigern auf int.
Ein Zeiger auf ein int-Array sieht so aus:void func(int (*arr)[3]);
-
blurry333 schrieb:
argv[0] argv[1] argv[2] . . usw
werden mit char** argv übergeben
Ja und? argv verweist auf (das erste Element) ein(es) Zeiger-Array(s). Bei
int foo[5][7];
gibt es kein Zeiger-Array. foo ist ein Array von Arrays. Du kannst jetzt noch ein Zeiger-Array einführen:
int* za[] = { foo[0], foo[1], foo[2], foo[3], foo[4] }; int** p = za;
dann zeigt p aber nicht auf irgendwas in foo sondern auf das erste Element des Zeiger-Arrays za.
Haben wir's jetzt?
-
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.