Forum: Mikrocontroller und Digitale Elektronik Pointer Anfängerfrage


von Michael (Gast)


Lesenswert?

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 */

von Michael (Gast)


Lesenswert?

oben vergessen:
#define MONTHS 12 // number of months in a year

von Kellernerd (Gast)


Lesenswert?

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.

von Herr Kaiser (Gast)


Lesenswert?

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?

von Michael (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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))

von Dirk B. (dirkb2)


Lesenswert?

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] )

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Michael schrieb:
> Ich dachte die Elemente stehen hintereinander im
> Speicher
Tun sie ja auch.
1
             | arr[0][0] | arr[0][1] | arr[1][0] | arr[1][1] ...
2
             |_ _________|___________|___________|__________
3
Memmoryaddr. |    0x00   |    0x01   |    0x02   |    0x03   |

von Michael (Gast)


Lesenswert?

&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]);

von J. F. (Firma: Père Lachaise) (rect)


Lesenswert?

Beachte den Datentyp (float).

von Dirk B. (dirkb2)


Lesenswert?

Michael schrieb:
> Die Reihenfolge ist ne andere.
Nein, ist sie nicht.

Bei dir sind die Adressen aufsteigend.
Im Beispiel von  Kaj G.  ist das nicht anders.
Er hat nur ein char arr[x][2];

Nach dem C-Standard wir der ganz rechte Index am schnellsten gewechselt.

von Kellernerd (Gast)


Lesenswert?

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).

von Kellernerd (Gast)


Lesenswert?

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 /* + ??? */);

von Kellernerd (Gast)


Lesenswert?

1
subtot += *(const float *)((const void *) rain /* + ??? */);
Ich gebe auf und werde VisualBasic Programmierer.

von achs (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.