Offene Cee FAQ / Zweidimensionales Feld Als Funktions Argument
 
StartSeite | OffeneCeeFAQ/ | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Alle Benutzer sind - wie immer - herzlich eingeladen, sich an der Ausarbeitung zu beteiligen.

Eine Kooperation von: KurtWatzka, HelmutSchellong

Frage

Eigentlich handelt es sich hier nicht um eine einzelne Frage, sondern um einen Fragenkomplex:

Frage1: Wie schreibe ich Funktionen, die zweidimensionale Arrays als Argumente annehmen, wenn die "Breite" zum Zeitpunkt der Übersetzung unbekannt ist?

Frage1b: Kann ein zwei- oder mehrdimensionaler Array als linearer Array aufgefasst und bearbeitet werden? Siehe /NichtlinearesFeldAlsLinearesFeld

Frage2: Nachdem Bezeichner von Arrays zu Zeigern zerfallen, was ist bei int array[NROWS][NCOLUMNS]; der Unterschied zwischen array und &array?

Frage3: Wie können sowohl statisch als auch dynamisch allozierte Arrays an die gleiche Funktion übergeben werden?

Oder, sozusagen als Metafrage hinter dem Problem: Wie werden zweidimensionale Arrays an Funktionen übergeben und warum sind Arrays eben doch keine Zeiger, wie es manchmal "zur Vereinfachung" behauptet wird?

Quellen

Verwandte Themen:

Antwort

Zu 1:
#define A2(a,b)  (a2p[(a)*sz2+(b)])

void fungdion(void *vp, int const sz2)
{
   int *a2p= vp;
   //...
   i= a2p[i1*sz2+i2];
   i+= A2(10,40);
   return;
}

Der C-Standard garantiert:

Frage
Welche Standard-Abschnitte garantieren das?
Operatoren == !=
...
[#6]  Two  pointers compare equal if both are null pointers,
both are pointers to the same object (including a pointer to
an  object  and  a  subobject at its beginning) or function,
both are pointers to one past the last element of  the  same
array object, or one is a pointer to one past the end of one
array object and the other is a pointer to the  start  of  a
different  array  object  that happens to immediately follow
the first array object in the address space.80)

80)Two objects may be adjacent in memory  because  they  are
   adjacent  elements  of a larger array or adjacent members
   of a structure with no padding between them,  or  because
   the  implementation  chose  to place them so, even though
   they are unrelated.  If prior invalid pointer operations,
   such as accesses outside array bounds, produced undefined
   behavior, the effect of subsequent  comparisons  is  also
   undefined.

--hs

Ich versteh das nicht ganz :-(( . Was garantiert mir, dass ein Compiler z. B. bei einem "char array[4][7];" nicht ein Padding-Byte ans Ende jedes SubArrays legt? -- hl
Der obenstehende Text allein garantiert das bereits eindeutig und zweifelsfrei:
Zwei Pointer sind gleich, wenn der eine um eins hinter ein Array-Objekt zielt
und der andere auf den Start eines anderen Array-Objektes, das sofort dem
erstgenannten im Adressraum folgt. 80)
80)Zwei Objekte können im Speicher aneinanderstoßend sein, weil sie die
   aneinanderstoßenden Elemente eines größeren Arrays sind.

An anderer Stelle sagt der Standard:
Arrays bestehen aus einer ununterbrochenen Folge von Elementen.
SubArrays sind Element-Objekte eines übergeordneten Arrays (Dimension).
Obwohl der Standard hier blöde formuliert und ebenso komische Worte wählt, habe ich dennoch keine Probleme, das eindeutig zu verstehen.
Der Standard muß doch nicht explizit sagen: "Hinter SubArrays darf kein Padding stehen."
Wozu? Der Standard verbietet doch durch massenhaft vorhandene andere Formulierungen, daß Padding innerhalb von Arrays vorkommen kann.
Beispw. steht oben "with no padding between them" nur bei Struktur, nicht aber darüber bei Array - warum denn wohl?! --hs

Danke, jetzt versteh ich's auch. -- hl

Der C-Standard garantiert nicht: Der Meinung bin auch ich, weil das unter J.2 steht und weil man bei einem vom Compiler angelegten Array mit konstanten Dimensionen nicht einfach andere Dimensionen benutzen kann. Der Compiler kann ja bestimmte spezielle Prozessor-Operationen benutzen, wenn er sieht, daß die Elementeanzahl unterhalb einer bestimmten Grenze liegt, die die Verwendung eben dieser speziellen Prozessor-Operation erlaubt. Wenn nun ein höherer Index auftaucht, dann knallt's eben. Allgemein gesagt, kann der Compiler die Zugriffsadresse nach eigenem Gutdünken berechnen (lassen), was den Weg dorthin angeht. Das gilt aber nur bei der Verwendung des jeweiligen Original-Anlege-Array-Namens. Ich bin der Meinung, daß man bei int a[4][5]; int *ap= &a[0][1]; mit ap[0..18] zugreifen darf, weil das schlicht etwas ganz anderes ist. Die Erkenntnis, daß eben dies etwas ganz anderes ist, ist wichtig. --hs

Zu 1b:

Hinter dieser Frage steckt folgender Gedanke:

TYP a[3][3];
TYP b[2][10];

void TypArrayAction(TYP *ptyp,int n) {
  while(n--) {
    TypAction(ptyp++);
  }
}
...
TypArrayAction((TYP *)a,9);
TypArrayAction((TYP *)b,20);

Welche Stellen im Standard garantieren, dass es zwischen den SubArrays kein störendes Padding gibt?

Siehe oben.
SubArrays sind Array-ELEMENTE!
Aus meiner Sicht kann jeder Pointer aus einem Array wie ein malloc-Pointer verwendet werden, sofern man korrektes Alignment beachtet und innerhalb der Byte-Grenzen bleibt! --hs

Zu 2:
Pointer-Kontext gegeben:
Typ von 'array'   :  int (*)[NCOLUMNS]
Typ von '&array'  :  int (*)[NROWS][NCOLUMNS]      //vor C89 ERROR!
Typ von 'array[x]':  int  *

Zu 3: siehe 1:

Sämtliche (Teil-)Ausdrücke im Zusammenhang mit array-Bezeichnern sind Pointer-Typen, nur mit Ausnahme von:

   sizeof(array_bezeichner)

   sizeof(array_bezeichner+0)   // (...) ist hingegen ein Pointer-Typ!
Volle Dereferenzierung bis hin zum Element(typ) ist hier nicht inbegriffen!

Siehe: http://www.schellong.com/de/c.htm#arr

Diese Sichtweise ist richtig, aber diese Darstellung ist eher noch verwirrender als das ungeliebte "zerfällt zu einem Zeiger auf das erste Array-Element", oder? &array_bezeichner ist eine Ausnahme, also sollte die Liste der Ausnahmen so formuliert werden, dass alle Ausnahmen darin vorkommen. -- kw
... Ausdrücke ... sind Pointer-Typen, mit Ausnahme von: &array_bezeichner: Das ist schlicht eine Falschaussage. Wieso soll '&array_bezeichner' überhaupt eine Ausnahme sein? Ausnahme wovon? Es ist ein Pointer-Typ, wie viele andere auch. --hs
Es ist eine Ausnahme von der Regel, dass ein array_bezeichner in einem Ausdruck den Typ Zeiger auf Arrayelement annimmt. Was ich kritisiere ist nicht die Aussage, dass der Typ von &array_bezeichner ein Zeigertyp ist, sondern die Überschrift. Eine nützliche Darstellung der Ausnahmen sollte so gewählt sein, dass &array_bezeichner eine Ausnahme darstellt. Dass &array_bezeichner von einem Zeigertyp ist, ist relativ unwichtig verglichen damit, dass &array_bezeichner nicht vom Typ Zeiger auf Zeiger auf Arrayelement ist. --kw
Ich denke, für den Lernenden sollte man einfach eine wirklich komplette Liste der Typen eines 2dim. Arrays aufzeigen. Das ist praxisnah.
Diskussion

Kurt, nach deiner letzten Äußerung in dclc schien es mir so, als wärst du der Meinung, dass eigentlich die FAQ ohnehin alles beantwortet. Möglicherweise tut sie das aber nur für denjenigen, der sich ohnehin schon sehr gut auskennt. Sollen wir diese Frage löschen, stehen lassen oder weiter verfolgen?

-- HelmutLeitner

Ich bin nicht der Ansicht, das die FAQ inhaltlich etwas offen läßt, aber die Klarheit der Formulierung ist sicher steigerbar. Weil die Formulierung aber von mir stammt und ich mir schon damals einige Mühe gegeben habe, mit der mir möglichen Klarheit zu formulieren, habe ich durchaus die Hoffnung, dass es hier gemeinsam gelingen sollte, eine Antwort zu finden, die nicht nur richtig sondern auch allgemeinverständlich und didaktisch gut ist. -- KurtWatzka

Zum ersten Lösungsvorschlag: Bei der 1. Teilfrage sind die meiner Meinung nach wesentlichen Aussagen, dass der Zugriff auf ein Element des Arrays 'von Hand' gelöst werden muss, und dass Implementation von C denkbar sind, die Arraygrenzen überprüfen, so dass die Lösung: "Behandle das Array von Arrays wie ein eindimensionales Array" nicht immer funktionieren muss. Welchen zusätzliche Beitrag die Übergabe der Anfangsaddresse als Zeiger auf void leisten soll ist mir nicht klar. (Der Typ muss in der Funktion bekannt sein, also gefällt mir "int *" als Typ des Arguments deutlich besser.) -- kw

Jedes beliebige Array kann als 1-dim Array benutzt werden. Der Standard garantiert das eindeutig und zweifelsfrei. Arraygrenzen kann man prüfen, indem man diese Infos mit übergibt - anders geht es hier nicht. Kein Compiler kann -hier- jemals diese Grenzen prüfen, denn bei Pointerübergabe mit 1-dim. geht die Array-Info verloren. void* habe ich gewählt, weil so die Fungdion universeller wird ohne Cast-Maßnahmen.

Zu "Sämtliche (Teil-)Ausdrücke im Zusammenhang mit array-Bezeichnern sind Pointer-Typen, nur mit Ausnahme von ...": Das erscheint mir eher noch abstrakter als die Originalformulierung. Auserdem verstehe ich nicht, warum die Dereferenzierung bis zum Elementtyp eine Ausnahme sein soll. Wie waere etwas in der Art von:

Wenn in einem Ausdruck der Bezeichner eines Arrays vorkommt, dann ist normalerweise sein Typ "Zeiger auf ein Element des Arrays" und sein Wert ein Zeiger auf das erste Element des Arrays". Diese Regel gilt aber nicht rekursiv. Das bedeutet, dass ein der Bezeichner eines Arrays von Arrays in einem Ausdruck normalerweise den Typ "Zeiger auf Array", nicht den Typ "Zeiger auf Zeiger" hat. Ausnahmen von dieser Umwandlungsregel sind die Operatoren sizeof und & --kw

int A[2][3]; i= A[1][0]; 'A[1][0]' hat hier also Pointer-Typ (welchen denn?) und soll somit keine Ausnahme sein. Das verstehe ich nicht.--hs

Es ist keine überraschende Tatsache, dass A[1][0] nicht von einem Zeigertyp ist. Der []-Operator ist so definiert, dass er äquivalent zu einer Dereferenzierung ist, und dass ein dereferenzierter Zeiger auf T vom Typ T ist, ist nicht überraschend. Insofern stellt der Operator [] oder der einstellige *-Operator keine Ausnahme von der Regel dar, welchen Typ array_bezeichner in einem Ausdruck hat. Natürlich ist es eine Ausnahme von der in der Überschrift formulierten Regel, aber ich halte diese Regel nicht für zweckdienlich zur Darstellung der Besonderheiten des Typs von array_bezeichner in einem Ausdruck. -- kw


Zur Sprache: Was mich in diesem Zusammenhang immer überrascht hat, ist dass für die Darstellung der Arrays und für die Erklärungen keine klarerer, mathematische Sprache verwendet bzw. gesucht worden ist (Standard, Bücher etc.). Die Begriffe Zeiger und Array tauchen in unterschiedlichsten Ebenen des Problems auf und bedürfen dann immer wieder erklärender Nebensätze. Ich habe das nicht komplett durchgedacht, aber ich könnte mir vorstellen, dass eine reichere Begriffswelt mit Array (1-n dim), Vektor(1-dim) und Matrix(2-n dim) der Klarheit dienen könnte. Z.B. könnte man dann Dinge sagen wie: "Jeder Array ist ein eindimensionaler Vektor von Vektorelementen. Ein Vektorelement kann aber wiederum ein Array sein, so dass mehrdimensionale Matrizen möglich sind... Der Arrayname ist vom Typ Zeiger auf Vektorelement... Wenn ein Array eine Matrix ist, dann enthalten die Vektorelemente normalerweise mehrere Arrayelemente. Bei Matrizen sind Vektorelementtyp und Arrayelementtyp unterschiedlich..." Man könnte dann auch ziemlich präzise von Dingen wie Arrayzeiger, Arrayelementzeiger, Vektorelementzeiger, Arraygröße, Arrayelementgröße, Vektorelementgröße, Vektorelementtyp o. ä. sprechen. Nur so eine Idee. -- hl

Die Sprachwahl ist meiner Ansicht nach sehr eindeutig. In der Mathematik unterscheidet man ueblicherweise nur zwischen Vektoren und Matrizen. In C bedarf es einer solchen Unterscheidung nicht, da C nunmal nur Arrays (auch Felder genannt) kennt. Ein Array ist eine Aneinanderreihung von Objekten gleichen Typs. Da ein Array selbst ein Objekt ist, gibt es eben automatisch Arrays von Arrays von... Weiter umschreibende Sprachelemente (Worte) einzuführen erscheint einerseits unnötig und andererseits noch verwirrender. -- zc


KategorieC KategorieCee
StartSeite | OffeneCeeFAQ/ | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 29. November 2007 8:44 (diff))
Suchbegriff: gesucht wird
im Titel
im Text