array als funktionsparameter

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Ma_No
Beiträge: 22
Registriert: 13.04.2013, 22:16
Benutzertext: Noch am Anfang. :)
Echter Name: Matthias Nowottnick

array als funktionsparameter

Beitrag von Ma_No »

Guten tag,
ich habe ein kleines Problem und hoffe ihr könnt mir helfen.
Ich bin noch ein kleiner anfänger im Umgang mit c++ die wichtigsten Grundlagen habe ich drauf
dennoch stosse ich oftmals noch auf meine Grenzen so wie jetzt.
Ich habe versucht ein kleines Programm zu schreiben bei dem mir ein Array in Form eines Koordinatensystems
gefüllt und ausgegeben wird jedoch scheitere ich daran dieses, mehrdimensional, als Funktionsparameter zu übergeben.
Ich bin mir nicht sicher warscheinlich wäre es leichter das einfach alles in eine Klasse zu packen aber ich würde trotzdem
gerne wissen wie ich das machen kann
Hier mein code:

Code: Alles auswählen

#include <iostream>
using namespace std;

char field[7][7];
void fill_field(char *field[][7]);
void show_field(char field[][7]);

int main()
{
    fill_field (&field);
    show_field(field);

}


void fill_field(char *field[][7])
{
    for (int x = 0; x != 7; x++)
    {
        for (int y = 0; y != 7; y++)
        {
            *field[x][y] = '.';
        }
    }
}

void show_field(char field[][7])
{
    for(int x = 0; x != 7; x++)
    {
        for (int y = 0; y != 7; y++)
        {
            cout << field[x][y];
        }

        cout << endl;
    }
}
ich erhalte immer den Compilerfehler: error: Can not convert 'char (*)[7][7]' to 'char (*)[7]' for argument '1' to 'void fill_field (char* (*)[7]'
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von dot »

&field liefert dir einen Pointer auf ein Array aus 7 Arrays aus 7 char, also einen char (*)[7][7].

void fill_field(char *field[][7]); deklariert eine Funktion, die einen Pointer auf ein Array aus 7 Pointern auf einzelne char als Parameter enthält. Vermutlich nicht wirklich, was du willst.

void show_field(char field[][7]); deklariert eine Funktion, die einen Pointer auf ein Array unbekannter Größe aus Arrays aus 7 char als Parameter erhält. Schon viel eher, was du willst.

Ich weiß, es sieht so aus, als müssten die Parameter dieser Funktionen eigentlich Arrays sein...aber rohe Arrays folgen in C++ leider äußerst arkanen Regeln, die im konkreten Beispiel von Funktionsparametern z.B. sicherstellen, dass Arrays in C++ effektiv by reference übergeben werden (genaugenommen schreibt der Compiler da deinen Code für dich um, ohne dir was davon zu sagen).
Meine Empfehlung wäre, entweder einfach ein typedef char field_t[7][7]; oder noch besser gleich anstatt roher Arrays durchgehend std::array zu benutzen. Das verhält sich dann so, wie man es intuitiv erwarten würde.
Ma_No
Beiträge: 22
Registriert: 13.04.2013, 22:16
Benutzertext: Noch am Anfang. :)
Echter Name: Matthias Nowottnick

Re: array als funktionsparameter

Beitrag von Ma_No »

Ähm ok also deine Antwort verstehe ich nicht ganz...
Aber ich habe mittlerweile herausgefunden wie ich es machen muss
ich muss es in der form "void fill_field(char (*field)[7][7])" deklarieren damit er den Zeiger auf das "ganze" array nimmt und nicht nur uf das erste Element
Benutzeravatar
Jonathan
Establishment
Beiträge: 2369
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Jonathan »

Ich kann dot nur zustimmen, es gibt einfach keinen vernünftigen Grund mehr, C-Arrays zu benutzen. Je eher du dich mit std::array, std::vector und vielleicht ein paar Spezialisierungen für mehrdimensionale vectoren auseinander setzt, desto eher wirst du glücklich werden.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: array als funktionsparameter

Beitrag von BeRsErKeR »

Jonathan hat geschrieben:Ich kann dot nur zustimmen, es gibt einfach keinen vernünftigen Grund mehr, C-Arrays zu benutzen. Je eher du dich mit std::array, std::vector und vielleicht ein paar Spezialisierungen für mehrdimensionale vectoren auseinander setzt, desto eher wirst du glücklich werden.
Das sehe ich anders. Gerade als Anfänger ist es ganz gut auch mal die Basics kennen zu lernen. Ob man sie später nutzt ist nicht entscheidend. Man kann beispielsweise theoretisch auch gänzlich ohne Pointer in C++ arbeiten - aber es ist auch nicht verkehrt zu wissen wie Pointer funktionieren. Man lernt durch solche Sachen auch oft wie bestimmte Dinge funktionieren und gerade auch wenn man auf solche Probleme wie hier trifft, hat das einen lehrreichen Charakter.

Es ist natürlich richtig auf std::array und Co zu verweisen. ;) Aber von Anfang an nur die STL zu kennen ohne zu wissen wie sowas überhaupt funktioniert / funktionieren könnte, ist mMn nicht unbedingt auch der beste Weg.

Gerade in Hinblick darauf, dass man vielleicht später auch mal mit C oder einer vergleichbaren Sprache was machen will. Da steht man dann ziemlich dumm da, wenn man nur die fertigen Klassen kennt.
Ohne Input kein Output.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von dot »

Ich finde auch, dass Arrays zu den wichtigen Grundlagen gehören. Allerdings sind die Regeln von C und C++ in diesem konkreten Fall sehr verwirrend, was ja überhaupt erst der Grund für die Existenz dieses Thread ist. Ich wollte einfach nur vermeiden, ihn zum jetzigen Zeitpunkt mit unnötigen Details vom Wesentlichen abzulenken. Aber hier kurz zum Verständnis was wirklich passiert:

int a[3]; deklariert ein Array aus 3 int. int a[4][3]; deklariert ein Array aus 4 Arrays aus jeweils 3 int. void f(int* a); deklariert eine Funktion mit einem Pointer auf einen int als Parameter; so weit so gut. Jetzt kommt's: void f(int a[3]); deklariert ebenfalls eine Funktion mit einem Pointer auf einen int als Parameter und keine Funktion mit einem Array als Parameter. C und C++ haben speziell für diesen Fall eine Regel, die besagt, dass die Deklaration jedes Funktionsparameters, der als Arrays deklariert ist, in eine Deklaration eines entsprechenden Pointers umgewandelt wird (man spricht auch von "array to pointer decay"). Einfaches Beispiel zum selbst ausprobieren:

Code: Alles auswählen

void f(int* a)
{
}

void f(int a[3]) // fehler: funktion bereits definiert
{
}
Wenn diese Regel nun mit mehrdimensionalen Arrays und der impliziten Konvertierung von Arrays in Pointer auf das erste Element des Arrays zusammentrifft, wird das meiner Erfahrung nach selbst für fortgeschrittene Programmierer schnell mal völlig undurchschaubar.
Gehen wir erst mal ein paar Basics durch:

Code: Alles auswählen

int a[3]; // Array aus 3 int
int* b = a; /* == */ int* b = &a[0]; // Pointer auf erstes Element eines int[3]
int (*c)[3] = &a; // Pointer auf Array aus 3 int

int aa[4][3]; // Array aus 4 Arrays aus 3 int
int (*bb)[3] = aa; /* == */ int (*bb)[3] = &aa[0]; // Pointer auf erstes Element eines int[4][3]
int* cc = aa[1];  /* == */ int* cc = &aa[1][0]; // Pointer auf erstes Element des zweiten Elements eines int[4][3]
int (*dd)[4][3] = &aa; // Pointer auf Array aus 4 Arrays aus 3 int
Ok, gut. Das hier

Code: Alles auswählen

int a[4][3];
ist also nicht das selbe wie

Code: Alles auswählen

int (*a)[3];
während das hier

Code: Alles auswählen

void f(int a[4][3]);
das selbe ist wie

Code: Alles auswählen

void f(int (*a)[3]);
Referenzen funktionieren mehr oder weniger analog zu den Pointern.

Wenn du nun ein zweidimensionales int[4][3] Array an eine Funktion übergeben willst, hast du prinzipiell die folgenden Möglichkeiten:

Code: Alles auswählen

void f1(int a[4][3]); /* == */ void f(int (*a)[3]); // Pointer auf erstes Element deines Arrays
void f2(int (*a)[4][3]); // Pointer auf dein Array
void f3(int (&a)[4][3]); // Referenz auf dein Array

// Aufruf:
int a[4][3];

f1(a); /* == */ f1(&a[0]);
f2(&a);
f3(a);
Einfacher wird es schon durch ein typedef:

Code: Alles auswählen

typedef int blub[4][3];
void f1(blub a); // Pointer auf erstes Element deines Arrays
void f2(blub* a); // Pointer auf dein Array
void f3(blub& a); // Referenz auf dein Array

// Aufruf:
blub a;

f1(a); /* == */ f1(&a[0]);
f2(&a);
f3(a);
Aber richtig intuitiv wird es erst, wenn man das Array in ein struct verpackt:

Code: Alles auswählen

struct blub
{
  int a[4][3];
};

void f1(blub a); // Kopie deines struct
void f2(blub* a); // Pointer auf dein struct
void f3(blub& a); // Referenz auf dein struct

// Aufruf:
blub a;

f1(a);
f2(&a);
f3(a);
std::array ist nichts anderes als so ein struct mit einem Array drin... ;)
Zuletzt geändert von dot am 23.11.2013, 13:26, insgesamt 1-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Super Beitrag; danke!

Gerade, weil ich schon lange nur noch eindimensionale Arrays benutze hat mir das Einiges wieder ins Gedächtnis gerufen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: array als funktionsparameter

Beitrag von eXile »

Krishty hat geschrieben:weil ich schon lange nur noch eindimensionale Arrays benutze
Könntest du kurz erklären, was deine Beweggründe dafür waren?
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Genervt hat es mich, als ich dauernd array[y][x] mit array[x][y] verwechselt habe, und die Programme dabei nicht muckten sondern mit den transponierten Daten weiterarbeiteten, und ich nie wusste, wo ich den Fehler suchen sollte.

Als die Speicherauslegung wichtig wurde und ich mir nie merken konnte wie C das jetzt macht, bin ich auf arr[y * width + x] umgestiegen.

Zuletzt lassen sich 2D-Arrays nicht dynamisch allokieren – spätestens dann muss man sowieso alles selber machen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von dot »

Krishty hat geschrieben:Zuletzt lassen sich 2D-Arrays nicht dynamisch allokieren – spätestens dann muss man sowieso alles selber machen.
Hängt davon ab, wenn beide Dimensionen dynamisch sein sollen, dann nicht, wenn nur eine dynamisch sei soll, dann geht's... ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Nur, wenn es auch genau die eine ist, die du dynamisch brauchst. Yay.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Noch ein Gejammer darüber, wie katastrophal miskonzipiert multidimensionale C-Arrays sind. Nehmen wir an:

  char const namesPerLanguageAndDay[2][7][4] = { // oder war es [4][7][2]?
    { "mon", "tue", "wed", "thu", "fri", "sat", "sun" },
    {  "Mo",  "Di",  "Mi",  "Do",  "Fr",  "Sa",  "So" }
  };


Wenn ich durch die Namen einer bestimmten Sprache iterieren möchte, würde ich jetzt erwarten:

  auto const & englishNames = namesPerLanguageAndDay[0];
  for(auto toName = begin(englishNames); toName < end(englishNames); ++toName) {
    printf(*toName);
  }


Das kompiliert nicht einmal. Ich wette, dass keiner aus dem Kopf sagen kann, wie es richtig geht:

  char const (&englishNames)[4][7] = namesPerLanguageAndDay[0]; // WAT why u fuckin prick cast me arr[0] to char *
  for(char const (*toName)[7] = englishNames; toName < end(englishNames); ++toName) { // Ich mache einen Zeiger; warum kommt kein & davor? KILL IT KILL IT WITH FIRE
    printf(*toName);
  }


Hat da jemand for each getestet? Dürfte ja eigentlich nicht gehen; es sei denn, sie haben begin(T (&)[a][c]) für alle Dimensionen implementiert, oder?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von dot »

Code: Alles auswählen

#include <iostream>

const char namesPerLanguageAndDay[2][7][4] = {
	{ "mon", "tue", "wed", "thu", "fri", "sat", "sun" },
	{ "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" }
};


auto& englishNames = namesPerLanguageAndDay[0];

int main()
{
	for (auto& n : englishNames)
		std::cout << n << '\n';

	for (auto n = std::begin(englishNames); n != std::end(englishNames); ++n)
		std::cout << *n << '\n';

	return 0;
}
funzt bei mir einwandfrei... ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Du hast recht … WhyTF hat der das gestern zu char * gecastet? Okaaaaaaaaay
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: array als funktionsparameter

Beitrag von BeRsErKeR »

Krishty hat geschrieben:Noch ein Gejammer darüber, wie katastrophal miskonzipiert multidimensionale C-Arrays sind. Nehmen wir an:

  char const namesPerLanguageAndDay[2][7][4] = { // oder war es [4][7][2]?
    { "mon", "tue", "wed", "thu", "fri", "sat", "sun" },
    {  "Mo",  "Di",  "Mi",  "Do",  "Fr",  "Sa",  "So" }
  };
Frisst der Compiler das? Soweit ich das verstehe ist [4] die Länge des Strings. Müsste er dann nicht bei "Mo", "Di", etc. meckern? Bei ein- und zweidimensionalen Arrays hat er sich da immer affig wenn es nicht genau passt, soweit ich mich recht erinnere.
Ohne Input kein Output.
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: array als funktionsparameter

Beitrag von Krishty »

Nein; in C/C++ werden Arrays implizit am Ende mit 0 aufgefüllt.

  int x[100] = { 1 };

wird dir eine 1 erzeugen und danach 99 Nullen. Das kann eine furchtbare Fehlerquelle sein wenn man Deklaration und Definition im Einklang halten muss :( Aber ebenso wird

  char x[4] = "Mo";

dir implizit eine zweite Null ans Ende hängen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: array als funktionsparameter

Beitrag von BeRsErKeR »

Krishty hat geschrieben:Aber ebenso wird

  char x[4] = "Mo";

dir implizit eine zweite Null ans Ende hängen.
Ist das schon immer so? Ich möchte sehr stark behaupten, dass das vor ein paar Jahren noch nicht vom Compiler gefressen wurde. Gab es da eine neue Festlegung oder hab ich einfach nur ein mieses Gedächtnis?

Nachtrag: Hm habe gerade Dokumente von 1997 gefunden, wo das wohl auch schon ging. Sehr merkwürdig. Vielleicht konnte es das MSVC++6.0 damals einfach aus Blödheit nicht. :D
Ohne Input kein Output.
Antworten