Pointer Anfängerfrage

Gast #5280941
Hallo,

ich lese gerade den "C Primer Plaus" und mache die erste Übung zu 
Pointern und bin schon am verzweifeln.
1
const float rain[YEARS][MONTHS] = { { 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2,
2
      0.2, 0.4, 2.4, 3.5, 6.6 }, { 8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2,
3
      0.9, 0.3, 0.9, 1.4, 7.3 }, { 9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2,
4
      0.2, 1.1, 2.3, 6.1, 8.4 }, { 7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4,
5
      0.0, 0.6, 1.7, 4.3, 6.2 }, { 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0,
6
      0.0, 0.0, 1.3, 2.5, 5.2 } };
7
  int year, month;
8
  float subtot, total;
9

10
  printf(" YEAR RAINFALL (inches)\n");
11
  for (year = 0, total = 0; year < YEARS; year++) { // for each year, sum rainfall for each month
12
    for (month = 0, subtot = 0; month < MONTHS; month++)
13
    subtot += rain[year][month];
14
    printf("%5d %15.1f\n", 2000 + year, subtot);
15
    total += subtot; // total for all years
16
  }



Es soll die Zeile "subtot += rain[year][month]" durch Pointer ersetzt 
werden.
Ich habe folgende Lösung die nicht funktioniert:
subtot +=  *(rain + ((year * MONTHS) + month) ) ;

Wie bin ich auf diese Lösung gekommen?
Dies steht im Buch:
*(dates + 2) /* value of the 3rd element of dates */
Gast
Hallo!
Das Beispiel mit dates aus Deinem Buch war fuer ein
1D Array. In Deinem Fall musst Du das Prinzip zweimal
anwenden:
*(rain + year) ergibt das 1D Array mit den monatlichen Regenfaellen
fuer ein gegebens Jahr (year).
*(*(rain + year) + month) ergibt dann das entsprechende Element
aus diesem Array fuer einen gegebenen Monat (month).
Du benoetigst also:
subtot += *(*(rain + year) + month);
Das sollte das Buch aber eigentlich erlaeutern, falls es etwas taugt.
Gast
Das dazugehörige Basiswissen ist, dass in C die Schreibweise Array[] 
äquivalent ist zu *(Array + Index). Sprich: setzt du einen Pointer ptr 
auf das erste Arrayelemente, kannst du mit einem Inkrement des Pointers 
zum nächsten springen. In deinem Fall sind es zwei verschachtelte 
Arrays. Bringt dich das weiter?
Gast
Kellernerd schrieb:
> subtot += *(*(rain + year) + month);

Diese Antwort ist richtig.

Leider verstehe ich es nicht. Ich glaube ich hab mir die Sache einfach 
falsch vorgestellt. Ich dachte die Elemente stehen hintereinander im 
Speicher und ich muss abzählen.
Auf diese Lösung wäre ich in 100 Jahren nicht gekommen.
rain ist ein Array von einem Array von const float.

rain[0] gibt die Adresse vom ersten Jahr und ist vom Typ "Zeiger auf 
const float"
rain[1] gibt die Adresse vom zweiten Jahr

rain gibt die Adresse vom ersten Array und ist vom Typ "Zeiger auf Array 
12 von const float"


Da Array[index] und *(Array+index) identisch sind, hast du für
rain[year][month] dann *(rain[year]+month)) und schließlich
*(*(rain+year)+month))
Anmerkungen:

Da Array[index] und *(Array+index) identisch sind, und für die Addition 
das Kommutativgesetz gilt, kann man auch *(index+Array)  schreiben.
Darum ist auch index[Array] in C erlaubt

rain und rain[0] ergeben dieselbe Adresse, sind aber von 
unterschiedlichem Typ.

rain+1 zeigt auf das zweite Jahr, während rain[0]+1 auf den zweiten 
Monat im ersten Jahr zeigt (wie &rain[0][1] )
Gast
&rain[0][0] = 0028FE30
&rain[0][1] = 0028FE34
&rain[0][2] = 0028FE38
&rain[0][3] = 0028FE3C
&rain[0][4] = 0028FE40
&rain[0][5] = 0028FE44
&rain[0][6] = 0028FE48
&rain[0][7] = 0028FE4C
&rain[0][8] = 0028FE50
&rain[0][9] = 0028FE54
&rain[0][10] = 0028FE58
&rain[0][11] = 0028FE5C
&rain[1][0] = 0028FE60
&rain[1][1] = 0028FE64
&rain[1][2] = 0028FE68
&rain[1][3] = 0028FE6C
&rain[1][4] = 0028FE70
&rain[1][5] = 0028FE74
&rain[1][6] = 0028FE78
&rain[1][7] = 0028FE7C
&rain[1][8] = 0028FE80
&rain[1][9] = 0028FE84
&rain[1][10] = 0028FE88
&rain[1][11] = 0028FE8C
&rain[2][0] = 0028FE90
&rain[2][1] = 0028FE94
&rain[2][2] = 0028FE98
&rain[2][3] = 0028FE9C
&rain[2][4] = 0028FEA0
&rain[2][5] = 0028FEA4
&rain[2][6] = 0028FEA8
&rain[2][7] = 0028FEAC
&rain[2][8] = 0028FEB0
&rain[2][9] = 0028FEB4
&rain[2][10] = 0028FEB8
&rain[2][11] = 0028FEBC
&rain[3][0] = 0028FEC0
&rain[3][1] = 0028FEC4
&rain[3][2] = 0028FEC8
&rain[3][3] = 0028FECC
&rain[3][4] = 0028FED0
&rain[3][5] = 0028FED4
&rain[3][6] = 0028FED8
&rain[3][7] = 0028FEDC
&rain[3][8] = 0028FEE0
&rain[3][9] = 0028FEE4
&rain[3][10] = 0028FEE8
&rain[3][11] = 0028FEEC
&rain[4][0] = 0028FEF0
&rain[4][1] = 0028FEF4
&rain[4][2] = 0028FEF8
&rain[4][3] = 0028FEFC
&rain[4][4] = 0028FF00
&rain[4][5] = 0028FF04
&rain[4][6] = 0028FF08
&rain[4][7] = 0028FF0C
&rain[4][8] = 0028FF10
&rain[4][9] = 0028FF14
&rain[4][10] = 0028FF18
&rain[4][11] = 0028FF1C


Die Reihenfolge ist ne andere.

Das ist die Ausgabe von:
printf("&rain[%i][%i] = %p\n", year,month, &rain[year][month]);
Gast
Michael schrieb:
> [...]
> falsch vorgestellt. Ich dachte die Elemente stehen hintereinander im
> Speicher und ich muss abzählen.

Die Elemente stehen, wie von Dir vermutet und von anderen Kommentatoren 
bereits erlaeutert, tatsaechlich hintereinander im Speicher. Die von Dir 
angenommene Auswahl mittels
Gesamtindexberechnung gilt allerdings nur in eindimensionalen Arrays.
Du koenntest also ein 1D Array erzeugen und dort alle Niederschlagswerte
hintereinander hineinschreiben (= recht unuebersichtlich) oder aber auch
Deine Adresse eines zweidimensionalen Arrays (rain) als Adresse eines
eindimensionalen Arrays uminterpretieren (casten), um den von Dir
angenommen Zugriff zu realisieren:
1
subtot += *((const float *) rain + year * MONTHS + month);
Hier musst Du jedoch Vorwissen haben, wie die Werte eines
zweidimensionalen Arrays bei der Sprache C im Speicher abgelegt werden
(reihenweise oder spaltenweise) und ausserdem diese haessliche
Multiplikation mit einer Konstanten hinzufuegen. Schlimmer wird es
noch bei hoeheren Dimensionen (wenn Du z.B. noch Tag und Stunde 
haettest).
Das mehrdimensionale Array ist hier eleganter und leichter 
lesbar/verstaendlich.

Das Umschreiben in einen Zugriff mittels Zeiger soll dem Leser des
Buches wohl die Zusammenhaenge erlaeutern, denn es resultiert in
schlechterer Lesbarkeit und mehr Tipparbeit. Falls dies wirklich das
erste Beispiel zu Zeigern ist, ist der Buchautor etwas fies.

Ich wuerde Dir (bei Interesse) empfehlen, mehrere minimale
Beispielprogramme mit unterschiedlichen Arraydimensionen und
Zugriffsarten auszuprobieren  und diese genau mittels Debugger auf ihr 
Speicherlayout hin zu untersuchen.
Eeine spannende Erweiterung waere
z.B. ein Cast/Zugriff mittels (void *), so dass keine Kenntnis
mehr ueber die Groesse der Elemente im Array besteht und Du dann
entsprechend Zeigerarithmetik mit Bytes machen musst:
1
subtot += *((const void *) rain /* + ??? */);
.
Mit der Printf-Direktive %p kannst Du Dir Adressen ausgeben lassen.
Das ist fuer Experimente bei der Adressberechnung sehr hilfreich.
Z.B die Adresse Deines Arrays (rain) oder die eines Arrayelementes:
1
printf ("%p\n%p\n%p\n", rain, &rain, &rain[2][2]);
Wenn Du begruenden kannst, warum rain und &rain den
gleichen Wert liefern, hast Du den Themenkomplex recht gut verstanden.
Viel Spass beim Experimentieren und lass Dich nicht von der
Adressrandomisierung verwirren, bzw. schalte sie fuer Deine
Beispiele ab, da ansonsten Dein Array bei jedem Programmstart an einer
anderen Anfangsadresse liegt (dies ist eigentlich ein Schutz, damit
Angreifer die Adressen nicht so leicht erfahren und im Fall von
Programmierfehlern ausnutzen koennen).
Gast
Sorry, fuer das void* Beispiel muss es heissen
(die Daten auf die gezeigt wird sollen ja als float interpretiert 
werden):
1
subtot += *(float *)((const void *) rain /* + ??? */);
Gast
Kellernerd schrieb:
> subtot += *(const float *)((const void *) rain /* + ??? */);

Naja, man muss dabei sagen, dass Michaels Aufgabenstellung sinnlos ist 
und wohl nur der Erforschung der Anordnung dient. Die Originalzeile ist 
ja lesbar und gut:

Michael schrieb:
> Es soll die Zeile "subtot += rain[year][month]" durch Pointer ersetzt
> werden.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 8 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.