Forum: PC-Programmierung Pointer auf Array


von Chris (Gast)


Lesenswert?

Hallo,

ich möchte einen Pointer erstellen der mir auf die Adresse eines Arrays 
zeigt.
1
#include <avr/io.h>
2
3
volatile uint8_t data[5][6];
4
volatile uint16_t *data_pointer = &data;
5
6
int main(void)
7
{
8
9
}

Der Pointer wird auch richtig initialisiert, nur erhalte ich eine 
Compilerwarnung:
initialization from incompatible pointer type [enabled by default]

Was wäre denn der kompatible Pointer-Datentyp?

Danke

Chris

: Verschoben durch Admin
von Peter II (Gast)


Lesenswert?

Chris schrieb:
> Was wäre denn der kompatible Pointer-Datentyp?

zeiger auf Array

volatile uint16_t[6] *data_pointer = &data;

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> volatile uint16_t[6] *data_pointer = &data;

Schön wärs. Ist aber
  volatile uint16_t (*data_pointer)[6] = &data;

von Chris (Gast)


Lesenswert?

@ A.K. Funktioniert leider auch nicht.
Der Compiler (AtmelStudio) meldet immer noch:

initialization from incompatible pointer type [enabled by default]

Chris

von Mark B. (markbrandis)


Lesenswert?

Wenn das Array vom Typ uint8_t ist, dann muss natürlich auch der Zeiger 
vom Typ uint8_t sein.

von Hans (Gast)


Lesenswert?

Wie wärs mit:
1
volatile uint8_t data[5][6];
2
volatile uint8_t *data_pointer = data;

von Chris (Gast)


Lesenswert?

Hallo Mark,

ich brauch aber die Adresse vom Array, also mit uint8_t komm ich da 
nicht weit.

Chris

von (prx) A. K. (prx)


Lesenswert?

Hab einen Fehler korrigiert, die anderen abgeschrieben. Also
  volatile uint8_t (*data_pointer)[6] = data;
oder
  volatile uint8_t (*data_pointer)[5][6] = &data;
Je nachdem, was du genau gemeint hast.

: Bearbeitet durch User
von Chris (Gast)


Lesenswert?

Jetzt motzt er nicht mehr. :-)
Danke A.K. und alle anderen.

Chris

von Bastler (Gast)


Lesenswert?

sizeof(uint8_t*)!=sizeof(uint8_t)

von Chris (Gast)


Lesenswert?

Hallo,

jetzt war ich wohl etwas vorschnell.

> A.K. schrieb:
> volatile uint8_t (*data_pointer)[5][6] = &data;

Aber dann ist mein Pointer ja wieder ein uint8_t und kann somit keine 
Adresse aufnehmen. Wenn ich uint16_t daraus mache erhalte ich wieder die 
genannte Compilerwarnug.

von Chris (Gast)


Lesenswert?

Hallo Bastler,

ich möchte nicht die Größe des Arrays, sondern die Anfangsadresse des 
Arrays im Pointer haben.

von Max H. (hartl192)


Lesenswert?

Chris schrieb:
> Aber dann ist mein Pointer ja wieder ein uint8_t und kann somit keine
> Adresse aufnehmen. Wenn ich uint16_t daraus mache erhalte ich wieder die
> genannte Compilerwarnug.
Pointer sind immer gleich groß, egal auf was sie Zeigen. Mit dem 
uint8_t* sagst du nur, dass an der Adresse auf die der Pointer zeigt 
eine 8bit breite unsigned integer steht.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Chris schrieb:
> Hallo,
>
> jetzt war ich wohl etwas vorschnell.
>
>> A.K. schrieb:
>> volatile uint8_t (*data_pointer)[5][6] = &data;
>
> Aber dann ist mein Pointer ja wieder ein uint8_t und kann somit keine
> Adresse aufnehmen.

Unsinn. Nimm dein C-Buch nochmal in die Hand. Dein Pointer ist ein 
Pointer, und der kann jede Adresse aufnehmen. Der Elementtyp des Ziels 
des Zeigers ist 8 Bit breit, nicht der Zeiger.

> Wenn ich uint16_t daraus mache erhalte ich wieder die
> genannte Compilerwarnug.

Weil deine Array-Elemente vom Typ uint8_t sind und nicht vom Typ 
uint16_t.

Chris schrieb:
> ich möchte nicht die Größe des Arrays, sondern die Anfangsadresse des
> Arrays im Pointer haben.

Völlig falsch verstanden. Er wollte dir damit sagen, daß die Größe eines 
uint8_t* nichts, aber auch gar nichts mit der Größe eines uint8_t zu tun 
hat.

: Bearbeitet durch User
von Bastler (Gast)


Lesenswert?

Es ging um "ein uint8_t passt nicht wegen Adresse von Array". Wenn der 
Compiler keinen uint16_t* mit einer Adresse eines uint8_t initialisieren 
will, dann ist das verständlich, oder? Ich wollte eigentlich nur zum 
Nachdenken über C-Typen anregen. Weil "beim Selberdenken lernt man 
mehr".

von Bastler (Gast)


Lesenswert?

Danke Rolf!

von Chris (Gast)


Lesenswert?

Ich weiß was Ihr mir damit sagen wolltet. @ Rolf und Bastler.
Aber das löst ja mein Problem noch nicht.
Mal anders herum gefragt.

Wie erzeuge ich eine Variable (muss ja noch nicht mal ein Pointer sein.) 
Der die Anfangsadresse eines zweidimensionalen Arrays (uint8_t) enthält 
ohne das AtmelStudio meckert?
Muss doch möglich sein...

von Max H. (hartl192)


Lesenswert?

Versuchs mal den Pointer explizit zu casten.

von (prx) A. K. (prx)


Lesenswert?

Chris schrieb:
> Muss doch möglich sein...

Willst du zusätzlich zu 
Beitrag "Re: Pointer auf Array" noch eine 
Variante haben? Gerne:
  volatile uint8_t *data_pointer = &data[0][0];
In jedem der 3 Fälle steht im Pointer der gleiche Wert drin, nur dessen 
Typ ist jedes Mal ein anderer.

Vermutlich bist du aber anderswo total auf dem Holzweg und kämpfst grad 
mit einem Haufen Antworten zur falschen Frage, kombiniert mit 
weitgehender Ahnungslosigkeit was C angeht.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

volatile uint8_t *data_pointer = data[0];

volatile uint8_t (*data_pointer)[6] = data;  // Das wilst du aber nicht

von Rolf M. (rmagnus)


Lesenswert?

Chris schrieb:
> Ich weiß was Ihr mir damit sagen wolltet. @ Rolf und Bastler.
> Aber das löst ja mein Problem noch nicht.

Warum nicht? Du sagtest doch, daß du keine Warnung mehr bekommen hast 
mit uint8_t.

> Wie erzeuge ich eine Variable (muss ja noch nicht mal ein Pointer sein.)
> Der die Anfangsadresse eines zweidimensionalen Arrays (uint8_t) enthält
> ohne das AtmelStudio meckert?

Oben wurden zwei Möglichkeiten genannt. Was stimmt mit denen nicht?

Oder um ebenfalls mal anders zu fragen: Was ist denn das, was du 
eigentlich machen willst?

von Chris (Gast)


Angehängte Dateien:

Lesenswert?

Vermutlich hast du Recht A.K.
Ich kapiers einfach nicht.
Siehe Anhang

von (prx) A. K. (prx)


Lesenswert?

Was Rolf oben durch die Blume ausgedrückt hat:
Besorg dir ein C Buch und lies es. Raten reicht nicht.

von Rolf M. (rmagnus)


Lesenswert?

Chris schrieb:
> Vermutlich hast du Recht A.K.
> Ich kapiers einfach nicht.
> Siehe Anhang

Der zeigt aber keine der von A.K. vorgeschlagenen Lösungen, sondern was 
völlig anderes. &data[5][6] ist nicht die Adresse des Arrays, sondern 
wäre theoretisch die Adresse von data[5][6], wenn es das denn gäbe.

von Markus F. (mfro)


Lesenswert?

Chris schrieb:
> Vermutlich hast du Recht A.K.
> Ich kapiers einfach nicht.
> Siehe Anhang
1
&data[5][6]
ist die Adresse des Elements der 6. Zeile und 7. Spalte eines 
zweidimensionalen Arrays, das nur 5 Zeilen und 6 Spalten hat. Ich würd' 
auch meckern, wenn ich Compiler wär'.

Die (Start-) Adresse eines Arrays ist die Adresse seines ersten 
Elements. Mithin:
1
&data[0][0]

von Chris (Gast)


Lesenswert?

Okay jetzt hab ich's.
volatile uint8_t *data_pointer = &data[0][0];

Das mit den [0][0] hast du mir aber bis zum Schluss vorenthalten A.K. 
;-)
Ich dachte nachdem du oben immer [5][6] geschrieben hast muss die Größe 
des Array angegeben werden.

Danke nochmal an alle.

von Chris (Gast)


Lesenswert?

Ja das wars Markus.

von (prx) A. K. (prx)


Lesenswert?

Chris schrieb:
> Das mit den [0][0] hast du mir aber bis zum Schluss vorenthalten A.K.

Weil das nicht wirklich die Antwort auf deine Frage war. Denn dieses 
Ergebnis ist genau genommen nicht die Adresse vom Array, auch nicht die 
Adresse der ersten Zeile vom Array (das waren die beiden ersten 
Varianten), sondern die Adresse des ersten Elements der ersten Zeile vom 
Array. Also die Adresse eines Zeichens, nicht eines Arrays.

von W.S. (Gast)


Lesenswert?

Chris schrieb:
> volatile uint8_t *data_pointer = &data[0][0];

Chris schrieb:
> Ja das wars Markus.

Nanana..
Ob's das war, kannst du ja mal feststellen, indem du auf 
*data_pointer[i][j] irgend einen Wert draufschreibst, aber bitte keine 
Festwerte für i und j, sonst optimiert dir der Compiler deinen Bug weg..

Also etwa sowas:

extern int bla(void);

int i, j;

 i = bla();
 j = bla();
 *data_pointer[i][j] = bla():

und dann schauen wir mal..

W.S.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Man muss in C unterscheiden zwischen

- einem Array
- einem Pointer auf ein Array
- einem Pointer auf das erste Element eines Arrays

Diese drei Dinge werden fälschlicherweise oft als gleich angesehen,
obwohl es drei völlig verschiedene Dinge sind.

1
uint8_t data[5][6];                        // Array data

In deinem Fall haben wir es sogar mit einem zweidimensionalen Array zu
tun, das streng genommen nichts anderes ist als ein eindimensionales
Array, dessen 5 Elemente eindimensionale (Unter-)Arrays mit jeweils 6
Elementen des Typs uint8_t sind.

Deswegen gibt es drei Möglichkeiten, einen Pointer auf den Anfang der
Variablen data zeigen zu lassen:

1
uint8_t  *pointer1a        = &data[0][0];  // Pointer auf das erste Element des ersten Unterarrays von data
2
uint8_t  *pointer1b        =  data[0];     // dto., verkürzte Initialisierung
3
4
uint8_t (*pointer2a)   [6] = &data[0];     // Pointer auf das erste Unterarray von data
5
uint8_t (*pointer2b)   [6] =  data;        // dto., verkürzte Initialisierung
6
7
uint8_t (*pointer3 )[5][6] = &data;        // Pointer auf das Array data

In allen drei Alternativen hat der Pointer den gleichen numerischen Wert
(nämlich die Anfangsadresse von data), aber jedesmal einen einen anderen
Typ (s. Kommentare). Zudem gibt es in den ersten beiden Fällen jeweils
zwei syntaktisch unterschiedliche, aber semantisch äquivalente
Möglichkeiten der Initialisierung. Das kommt daher, dass man allgemein
1
  &(PointerOderArrayAusdruck)[0]

auch einfach als
1
  (PointerOderArrayAusdruck)

schreiben kann (was man i.Allg. auch tut).

Welcher der oben aufgeführten Pointertypen für dich der richtige ist,
hängt davon ab, wozu du den Pointer hinterher benutzen möchtest. In den
meisten Fällen ist die zweite Alternative die passendste, aber eben
nicht immer.

Man kann natürlich zwischen den einzelnen Pointertypen nach Belieben
hin- und hercasten. Wo immer möglich, sollte man aber auf Casts
verzichten, weswegen es wichtig ist, für Variablen gleich von Anfang die
am besten passenden Datentypen zu wählen.

von Chris (Gast)


Lesenswert?

Danke für die ausführliche Erklärung yalu.
Eine Frage resultiert sich für mich aber dann daraus.

Wieso muss ich überhaupt einen Typ bei der Pointer-Deklaration angeben, 
wenn der sich erst durch die Definition/Initialisierung ergibt?

Chris

von Mark B. (markbrandis)


Lesenswert?

Chris schrieb:
> Wieso muss ich überhaupt einen Typ bei der Pointer-Deklaration angeben

Muss man in C streng genommen nicht. Es gibt ja auch den typlosen 
Zeiger: void *

Wenn man den verwenden will, muss man ihn freilich vorher zurecht 
casten.

> wenn der sich erst durch die Definition/Initialisierung ergibt?

Sagt wer?

von Chris (Gast)


Lesenswert?

... hm


und wie caste ich ihn zurecht damit Deklaration und tatsächlicher Typ 
auch zusammenpassen?

volatile uint8_t data[5][6];
volatile uint16_t *data_pointer = (uint16_t) &data[0][0];

-> Warning1: initialization makes pointer from integer without a cast

von Mark B. (markbrandis)


Lesenswert?

Ich hab so ein bisschen den Eindruck, wir drehen uns im Kreis... wie man 
die Warnung los wird, steht hier im Thread bereits. Fall Du einen void * 
Zeiger benutzen wolltest, hast Du es im obigen Beispiel nicht getan.

Die eigentliche Frage ist sowohl hier als auch eigentlich immer:
Was willst Du im Endeffekt erreichen?

Vergiss mal für einen Moment Deinen Lösungsansatz und beschreibe 
stattdessen die Aufgabe.

: Bearbeitet durch User
von Chris (Gast)


Lesenswert?

Den Eindruck habe ich auch Mark.

Ich wiederhole mich gerne nochmal:

Wie erzeuge ich eine Variable (muss ja noch nicht mal ein Pointer sein.)
Der die Anfangsadresse eines zweidimensionalen Arrays (uint8_t) enthält
ohne das AtmelStudio meckert?
Muss doch möglich sein...

Damit ich nicht total frustriert ins Bett gehe möchte ich gerne Euren 
Kommentar zu meinem (bisherigen) Pointer-Verständnis auf mich 
einprasseln lassen:

1. volatile uint8_t data[5][6];
... ist ein 8-Bit Array aus 30 Elementen.

2. volatile uint8_t *data_pointer = (uint16_t) &data[0][0];
.. liest sich für mich als ein Pointer der 8-Bit breit ist.

von Mark B. (markbrandis)


Lesenswert?

Chris schrieb:
> 2. volatile uint8_t *data_pointer = (uint16_t) &data[0][0];
> .. liest sich für mich als ein Pointer der 8-Bit breit ist.

Schon, aber der Cast auf uint16_t ist Quatsch.

von Chris (Gast)


Lesenswert?

Sorry ja, den Cast wollte ich eben weg-editieren... geht aber leider 
nicht, da ich nicht angemeldet bin.

von Chris (Gast)


Lesenswert?

Ohne den Cast bekomme ich auch keine Fehlermeldung

volatile uint8_t *data_pointer = &data[0][0];

Mich stört nur eines...
Ich Deklariere einen Pointer mit uint8_t obwohl der eine Adresse (16-Bit 
enthält).
Wenn man den Code nachvollziehen soll, ist das sicher nicht sehr 
hilfreich.

von Mark B. (markbrandis)


Lesenswert?

Folgendes Beispiel kompiliert ohne Warnungen:

1
#include <stdint.h>
2
#include <stdio.h>
3
4
volatile uint8_t data[5][6] = { {  0,  1,  2,  3,  4,  5 }, 
5
                                {  6,  7,  8,  9, 10, 11 }, 
6
                                { 12, 13, 14, 15, 16, 17 }, 
7
                                { 18, 19, 20, 21, 22, 23 }, 
8
                                { 24, 25, 26, 27, 28, 29 } };
9
volatile uint8_t *data_pointer = &data[0][0];
10
11
int main(void)
12
{
13
    uint8_t i;
14
    
15
    for (i = 0; i < 5*6; i++)
16
    {
17
        printf("%d ", *data_pointer);
18
        data_pointer++;
19
    }
20
    
21
    return 0;
22
}

und hat die folgende Ausgabe:
1
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Was sonst könnte man noch mit einem Zeiger auf ein Array anfangen 
wollen? Hm, ihn vielleicht an eine Funktion übergeben. Oder vielleicht 
seine Adresse zu Lehrzwecken ausgeben (braucht man in keinem realen 
Progamm jemals ;-)

Chris schrieb:
> Mich stört nur eines...
> Ich Deklariere einen Pointer mit uint8_t obwohl der eine Adresse (16-Bit
> enthält).

Die Deklaration richtet sich danach, worauf der Zeiger zeigt.
1
Zeigt er auf uint8_t  --> dann uint8_t *
2
Zeigt er auf uint16_t --> dann uint16_t *
3
Zeigt er auf uint32_t --> dann uint32_t *
4
Zeigt er auf float    --> dann float *
5
Zeigt er auf double   --> dann double *
6
Zeigt er auf char     --> dann char *

Is etz eigentlich ned soooo schwer. ;-)

: Bearbeitet durch User
von Chris (Gast)


Lesenswert?

Mark, vielen Dank für den Einsatz.

Dein Beispiel wird auch bei mir völlig korrekt ohne Warnung ausgegeben.

Nur wie gesagt:

uint8_t *data_pointer wird als 8-Bit deklariert. Im Watch Fenster auch 
so dargestellt, enthält aber 0x0200 eine 16-Bit Adresse.

von rmu (Gast)


Lesenswert?

Chris schrieb:
> Ohne den Cast bekomme ich auch keine Fehlermeldung
>
> volatile uint8_t *data_pointer = &data[0][0];
>

das weist den compiler an, data_pointer so zu machen, dass eine 
anwendung des "*" operators (dereferenzieren) einen "uint8_t" ergibt. 
wie gross "data_pointer" dann (in bytes/bits) ist hängt vor allem von 
der hardware-architektur ab, u.U. auch von anderen Faktoren.

von Chris (Gast)


Lesenswert?

> Zeigt er auf uint16_t --> dann uint16_t *

...aber &data[0][0] ist doch eine 16 Bit-Adresse?!?????????

HILFE ICH DREH DURCH

von rmu (Gast)


Lesenswert?

Chris schrieb:
>> Zeigt er auf uint16_t --> dann uint16_t *
>
> ...aber &data[0][0] ist doch eine 16 Bit-Adresse?!?????????
>
> HILFE ICH DREH DURCH

denk nicht so kompliziert. der compiler weiss, wie gross ein pointer 
sein muss, und kümmert sich selber darum. man gibt nur an, auf welchen 
typ der zeiger hinzeigt.

von Chris (Gast)


Lesenswert?

Der Compiler ja, aber derjenige der das bearbeiten muss?

Könnt Ihr mir zumindest in Sachen "Lesbarkeit" beipflichten?

von R. M. (rmax)


Lesenswert?

Nein, denn Du unterscheidest immer noch nicht zwischen der Länge des 
Zeigers und der Länge des Wertes, auf den er zeigt. Zeiger sind 
systemgegeben immer gleich lang (je nach CPU bzw. Programmiermodell 16, 
32 oder 64 Bit), egal auf welchen Datentyp sie zeigen. Deshalb ist bei 
der Deklaration nur die Länge des Zieldatentyps interessant, aber nicht 
die Länge des Zeigers selbst.

Zur Veranschaulichung kannst Du Dir ja mal sizeof() von verschiedenen 
Datentypen und den Pointern darauf ausgeben lassen.

: Bearbeitet durch User
von R. M. (rmax)


Lesenswert?

Oder nochmal mit anderen Worten:
1
uint8_t *
 ist die Adresse eines Bytes im Speicher, dessen Inhalt vom Program als 
8-Bit-Wert aufgefasst werden soll.
1
uint16_t *
 ist die Adresse eines Bytes im Speicher, dessen Inhalt zusamen mit dem 
Imhalt des nachfolgenden Bytes vom Programm als 16-Bit-Wert aufgefasst 
werden soll.
1
uint32_t *
 ist die Adresse eines Bytes im Speicher, dessen Inhalt zusammen mit dem 
Imhalt der drei nachfolgenden Bytes als 32-Bit-Wert aufgefasst werden 
soll.

"Die Adresse eines Bytes im Speicher" ist dabei immer gleich groß, die 
Pointer unterscheiden sich nur darin, als welcher Datentyp das Byte bzw. 
die Bytes an der hinterlegten Adresse behandelt werden sollen.

von Chris (Gast)


Lesenswert?

.. aber muss ein Zeiger nicht immer mindestens so Lange sein wie die 
Anzahl der Elemente auf die ich zeigen möchte?

von R. M. (rmax)


Lesenswert?

Nein, der Zeiger ist immer genauso lang, daß genau eine Adresse darin 
Platz hat, egal, ob er auf ein Byte, ein (mehrdimmensionales) Array, 
eine große Struct oder sonstwas zeigt. Er enthält immer nur die Adresse 
des ersten Bytes des ersten Elements und der Rest ergibt sich aus dem 
Datentyp.

: Bearbeitet durch User
von Chris (Gast)


Lesenswert?

Das meine ich in meinem Beispiel ja...
> "Er muss so lang sein das eine Adresse darin Platz hat"

Mit volatile uint8_t *data_pointer = &data[0][0]; erstelle ich (für mich 
gesehen, wegen dem uint8_t) einen 8-Bit Zeiger

von R. M. (rmax)


Lesenswert?

Nein, Du erzeugst einen Zeiger auf einen 8-Bit-Wert, der sich in der 
Größe nicht von irgendeinem anderen Zeiger unterscheidet.

von Chris (Gast)


Lesenswert?

R. Max schrieb:
> Nein, Du erzeugst einen Zeiger auf einen 8-Bit-Wert, der sich in
> der
> Größe nicht von irgendeinem anderen Zeiger unterscheidet.

Ganz genau. Ich gebe dir in allen Punkten recht.

Mich stört nur die Deklaration:

volatile uint8_t i; -> 8-Bit (auf meinem Zielsystem)
volatile uint16_t i; -> 16 Bit
volatile uint32_t i; -> 32 Bit

volatile uint8_t *data_pointer = &data[0][0]; -> *data_pointer nimmt in 
Wahrheit 16-Bit ein.

von R. M. (rmax)


Lesenswert?

Chris schrieb:
> volatile uint8_t *data_pointer = &data[0][0]; -> *data_pointer nimmt in
> Wahrheit 16-Bit ein.

Ja, denn "uint8_t *" ist ein anderer Datentyp als "uint8_t" und muß 
genug Platz bieten, um eine beliebige Adresse aus dem Adreßraum des 
Zielsystems aufnehmen zu können.

Auf aktuellen PCs nimmt "uint8_t *" übrigens sogar 64 Bit ein.

Am besten gehst Du jetzt schlafen und schaust Dir das morgen mit etwas 
Abstand nochmal an, vielleicht fällt dann der Groschen.

: Bearbeitet durch User
von Chris (Gast)


Lesenswert?

Jo, besser is das;-)

... bevorzuge halt das Prinzip "what you see is what you get"
.. und wenn ich dann lese:
> Auf aktuellen PCs nimmt "uint8_t *" übrigens sogar 64 Bit ein.

schön und gut, aber widerstrebt mir.

von Marc (Gast)


Lesenswert?

Es interessiert aber niemanden, was dir widerstrebt oder nicht.

Da kannst du noch so viel rumjammern und -heulen.

Das Problem liegt einfach an dir, nicht an C.

von Rolf M. (rmagnus)


Lesenswert?

Chris schrieb:
> ... bevorzuge halt das Prinzip "what you see is what you get"

Der Zeiger hat einen Zieltyp, der irgendwie angegeben werden muß. Wie 
würdest du das denn angeben? Also wie würde für dich ein Zeiger, der 
einfach die richtige Größe für Zeiger hat (also um jede beliebige 
Adresse aufzunehmen) und auf einen uint8_t zeigt, aussehen, damit es für 
dich verständlich ist?

> .. und wenn ich dann lese:
>> Auf aktuellen PCs nimmt "uint8_t *" übrigens sogar 64 Bit ein.
>
> schön und gut, aber widerstrebt mir.

Ein uint8_t* ist ein Zeiger auf einen 8-Bit-Wert, nicht einer, der 
selbst 8 Bit breit ist. Ich weiß gar nicht, wie man darauf kommen 
könnte, daß das die Breite des Zeiger sein soll. Diese von Hand 
verstellen zu können, wäre doch unsinning.
Preisfrage:
1
struct X
2
{
3
    uint32_t b;
4
    uint32_t c;
5
    uint32_t foo[123];
6
};
Wie groß ist ein struct X* ? Glaubst du wirklich, daß für die Adresse 
nun 500 Bytes reserviert werden, nur weil ein struct X halt 500 Bytes 
groß ist?

von (prx) A. K. (prx)


Lesenswert?

Chris schrieb:
>> Auf aktuellen PCs nimmt "uint8_t *" übrigens sogar 64 Bit ein.
>
> schön und gut, aber widerstrebt mir.

Sowohl die Sprache C also auch generell die innere Logik von 
Programmiersprachen sind erkennbar nicht mit Rücksicht auf deine Gefühle 
konstruiert wurden. Aufgrund der normativen Kraft des Faktischen bleibt 
dir freilich nur übrig, dich entweder daran zu gewöhnen, oder auf 
beispielsweise Gartenbau oder Kaninchenzucht umzusteigen.

von Max H. (hartl192)


Lesenswert?

Ist meiner Meinung nach kompletter Blödsinn und schwer lesbar, aber du 
kannst deinem uint8_t Poiner auch einen anderen Namen geben:
1
typedef uint8_t* Ein16BitLangerPointerAufEineUint8;
2
3
volatile uint8_t data[5][6] = { {  0,  1,  2,  3,  4,  5 },
4
                                {  6,  7,  8,  9, 10, 11 },
5
                                { 12, 13, 14, 15, 16, 17 },
6
                                { 18, 19, 20, 21, 22, 23 },
7
                                { 24, 25, 26, 27, 28, 29 } };
8
9
volatile Ein16BitLangerPointerAufEineUint8 data_pointer = &data[0][0];

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

PS: Man muss die Deklarationstechnik von C nicht mögen, um damit umgehen 
zu können. Die wurde damals in wohlmeinender Absicht so gebaut, ist aber 
letztlich völlig verkackt worden und führt stellenweise zu bizarren und 
unintuitiven Konstruktionen. Weil C sich aber durchgesetzt hat muss man 
sich nun entweder daran oder an besagte Rosen oder Karnickel gewöhnen.

Ok, manche sehen als dritten Weg an, um C einen Bogen zu machen und 
ziehen andere Programmiersprachen vor. Manche favorisieren Assembler, 
was hier gelegentlich zu erbitterten Gefechten führt. Andere bleiben 
lieber bei PCs, wo die Auswahl an Programmiersprachen etwas grösser ist 
als bei Mikrocontrollern.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

A. K. schrieb:
> PS: Man muss die Deklarationstechnik von C nicht mögen, um damit umgehen
> zu können. Die wurde damals in wohlmeinender Absicht so gebaut, ist aber
> letztlich völlig verkackt worden und führt stellenweise zu bizarren und
> unintuitiven Konstruktionen. Weil C sich aber durchgesetzt hat muss man
> sich nun entweder daran oder an besagte Rosen oder Karnickel gewöhnen.

Na ja, da mag zwar an der ein oder anderen Stelle was dran sein, aber 
diese Argumentation da zu bringen, wo's um Zeiger geht, paßt nicht so 
recht.

Zeiger sind ein fundamentales Konzept und sie funktionieren in jeder mir 
bekannten Programmiersprache, die dieses Konzept unterstützt, sehr 
ähnlich.

In Pascal gibts Zeiger als (z.B.)
1
TYPE ptr = ^Recordtyp;
2
3
Recordtyp^ = ADDR(Record)

In Modula II gibt's analog den POINTER Typ. Letztendlich alles dasselbe.

Selbst in Assembler muß ich wissen, auf welchen Datentyp eine als Zeiger 
verwendete Adresse zeigt, um z.B. beim Abwandern eines Arrays den 
richtigen Inkrement zu verwenden.

In Java gibt's zwar angeblich keine Zeiger, dafür eine 
"NullPointerException". Wieso eigentlich?

Der Umgang mit Zeigern als wesentliches Programmierkonzept muß eben 
verinnerlicht werden. Wer das nicht will, verliert ein wesentliches 
Gestaltungselement.

Wenn der Postler das Konzept der Hausnummern nicht versteht und seine 
Briefe immer in den falschen Kasten wirft, woran liegt's dann: am 
Konzept oder am Postler?

von (prx) A. K. (prx)


Lesenswert?

Markus F. schrieb:
> Na ja, da mag zwar an der ein oder anderen Stelle was dran sein, aber
> diese Argumentation da zu bringen, wo's um Zeiger geht, paßt nicht so
> recht.

Sei data_pointer ein Zeiger auf ein Array, nicht auf skalares Element 
davon. Weshalb muss die Deklaration dann mit den Typ anfangen, der den 
Inhalt des Arrays beschreibt, auf den der Zeiger verweist? Weshalb muss 
ein Zeiger auf eine Array so seltsame Blüten bilden wie "typ 
(*pointer)[size]"?

> In Pascal gibts Zeiger als (z.B.)

Aber die Deklarationstechnik in Pascal rollt den Typ konsequent von 
links nach rechts auf und ist damit intuitiv verständlich. Das kannst du 
direkt vorlesen, mach das mal in C.

von Dirk B. (dirkb2)


Lesenswert?

Auch wenn es schon nach langer Diskussion geklärt ist (dummerweise aber 
als richtig bestätigt wurde):

Chris schrieb:
> 2. volatile uint8_t *data_pointer = (uint16_t) &data[0][0];
> .. liest sich für mich als ein Pointer der 8-Bit breit ist.

NEIN!.

Es ist ein Pointer, der auf einen 8-Bit Wert zeigt.

Pointer sind so groß, dass sie jede mögliche Adresse vom System 
aufnehmen können.

Yalu X. schrieb:
> uint8_t  *pointer1a        = &data[0][0];  // Pointer auf das erste
> Element des ersten Unterarrays von data
> uint8_t  *pointer1b        =  data[0];     // dto., verkürzte
> Initialisierung
>
> uint8_t (*pointer2a)   [6] = &data[0];     // Pointer auf das erste
> Unterarray von data
> uint8_t (*pointer2b)   [6] =  data;        // dto., verkürzte
> Initialisierung
>
> uint8_t (*pointer3 )[5][6] = &data;        // Pointer auf das Array data
>
> In allen drei Alternativen hat der Pointer den gleichen numerischen Wert
> (nämlich die Anfangsadresse von data), aber jedesmal einen einen anderen
> Typ (s. Kommentare).

Ein Unterschied gibt es noch bei der Größe vom Element, auf die der 
Pointer zeigt:
sizeof(*pointer1) ist  1 // achte auf den *, gilt fuer a und b
sizeof(*pointer2) ist  6 // achte auf den *, gilt fuer a und b
sizeof(*pointer3) ist 30 // achte auf den *
Dies ist wichtig bei Pointerarithmetik

sizeof(pointer1), sizeof(pointer2) und sizeof(pointer3) ergeben 
denselben Wert (ohne *)

von Daniel A. (daniel-a)


Lesenswert?

A. K. schrieb:
> Die wurde damals in wohlmeinender Absicht so gebaut, ist aber letztlich
> völlig verkackt worden und führt stellenweise zu bizarren und
> unintuitiven Konstruktionen.

C ist die intuitivste sprache die ich kenne. Es ist immer alles so, wie 
es da steht. Pointer zeigen auf positionen im speicher und haben eine 
Einheit (datentyp), arrays haben eine adresse im speicher, eine grösse 
und eine einheit, datentypen haben eine grösse, jeder speicherbereich 
kann als daten belibigen types aufgefasst werden... Einfach himmlisch!

Wenn ich datentypen lese gehe ich folgendermassen vor:

1) Funktionspointer isolieren
2) Für jedes argument der gefundenen funktion schritt 1 widerholen
3) Bei der inbersten klammer von rechts nach links zu lesen anfanngen 
(beim identifier,falls vorhanden)
4) lese * als pointer, [] als array, type(*)(arguments) als funktion, 
const als unveränderbar.

Beispiel:
1
void(**const(*funcptr_t)[3])(int*x[3],void(*)());
2
3
Funktionspointer:
4
void(**const(*funcptr_t)[3])(int*[3][3],void(*)());
5
void(*)();
Lese:
Ein pointer funcptr_t auf ein array von 3 elementen eines 
unveränderbaren pointertypes der auf Funktionspointer zeigt, welcher als 
argumente ein array aus 3×3 pointern auf ein int und einen 
Funktionspointer ohne argumente welcher nichts zurückgiebt nimmt, und 
nichts zurückgiebt.

Einfach, oder?

Grösse:
Es ist ein pointer, somit ist seine grösse im speicher die eines 
pointers und entspricht sizeof(void*)

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Daniel A. schrieb:
> Einfach, oder?

Ja. Geradezu selbsterklärend trivial. ;-)

von jemand (Gast)


Lesenswert?

War Chris ein Troll?

von DJ T. (Gast)


Lesenswert?

Jetzt bin ich aber verwirrt!

Ich hätte jetzt gedacht, das hier
1
volatile uint8_t (*data_pointer)[6];
ist doch ein Array aus 6 Pointern, oder?

Wie kann man das dann diese Array mit "data" initialisieren, wie im 
genannten Beispiel:
1
volatile uint8_t (*data_pointer)[6] = data;

von Karl H. (kbuchegg)


Lesenswert?

DJ Tobsen schrieb:
> Jetzt bin ich aber verwirrt!
>
> Ich hätte jetzt gedacht, das hier
>
1
> volatile uint8_t (*data_pointer)[6];
2
>
> ist doch ein Array aus 6 Pointern, oder?

Das hier
1
volatile uint8_t * data_pointer[6];
wäre ein Array aus 6 Pointern, von denen jeder einzelne auf einen 
uint8_t (oder mehrere uint8_t, das kann man nicht ablesen) zeigt.


Das obige ist ein Pointer auf ein Array (mit 6 Elementen) vom Typ 
uint8_t

Die Klammern machen den Unterschied.

Fang beim Variablennamen an.
Dann geh nach rechts bis entweder
* nichts mehr rechts steht
* oder du auf eine schliessende Klammer stösst
Hast du soweit alles nach rechts abgearbeitet, dann mach dasselbe nach 
links, bis da wieder nichts mehr steht oder du auf eine öffnende Klammer 
stösst. Dann wechselst du wieder die Richtung, etc.
[] liest du als 'Array von'
() liest du als 'Funktion'
1
uint8_t (*data_pointer)[6];
1
data_pointer                ist ein
2
3
data_pointer)      Oops schliessende Klammer, also Richtung wechseln
4
5
*data_pointer)              ist ein Pointer
6
7
(*data_pointer)    Oops öffnende Klammmer, also Richtung wechseln
8
9
(*data_pointer)[ ]          ist ein Pointer auf ein Array
10
11
                  Weiter rechts steht nichts mehr, also links weiter
12
13
uint8_t (*data_pointer)[ ]  ist ein Pointer auf ein Array von uint8_t

wohingegen
1
uint8_t * data_pointer[6];
1
data_pointer                ist ein
2
data_pointer [ ]            ist ein Array
3
4
                weiter rechts steht nichts mehr, also Richtung wechseln
5
6
*data_pointer [ ]           ist ein Array von Pointern
7
8
uint8_t * data_pointer [ ]  ist ein Array von Pointern auf uint8_t

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Du kannst mal bei http://cdecl.org/ etwas rumspielen.

Die kennen aber die Typen aus stdint.h nicht.
(Welchen Typ man nimmt ist aber eigentlich egal)

von DJ T. (Gast)


Lesenswert?

Karl Heinz schrieb:
> Das obige ist ein Pointer auf ein Array (mit 6 Elementen) vom Typ
> uint8_t

Wußte ich gar nicht, danke!

von R. M. (rmax)


Lesenswert?

Dann ist das oben mehrfach wiederholte Beispiel
1
uint8_t data[6];
2
volatile uint8_t (*data_pointer)[6] = data;
aber trotzdem falsch, denn es müßte
1
volatile uint8_t (*data_pointer)[6] = &data;
heißen.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

R. Max schrieb:
> aber trotzdem falsch, denn es müßte

Nein, denn data ist im obigen Beispiel
  volatile uint8_t data[5][6];
folglich ist &data ein Pointer auf uint8_t[5][6], also
  volatile uint8_t (*data_pointer)[5][6] = &data;

Hingegen liefert
  volatile uint8_t (*data_pointer)[6] = data;
einen Pointer auf das erste Element von data[5][6], also
  volatile uint8_t (*data_pointer)[6] = &data[0];

Also je nach erwünschtem Pointer-Typ:
  volatile uint8_t (*data_pointer)[5][6] = &data;
  volatile uint8_t (*data_pointer)[6]    =  data;
  volatile uint8_t *data_pointer         = *data;

: Bearbeitet durch User
von R. M. (rmax)


Lesenswert?

Ah - ich hatte übersehen, daß data mal als eindimmensionales und mal als 
zweidimmensionales Array deklariert wurde.

von Dirk B. (dirkb2)


Lesenswert?

R. Max schrieb:
> Ah - ich hatte übersehen, daß data mal als eindimmensionales und mal als
> zweidimmensionales Array deklariert wurde.

data war immer ein 2D-Array (Ausnahme ist dein Post von 18.05.2015 
13:35)

von R. M. (rmax)


Lesenswert?

Ihr habt ja recht, da hatte ich mich wohl verlesen.
Sorry for the noise.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

A. K. schrieb:
> Aber die Deklarationstechnik in Pascal rollt den Typ konsequent von
> links nach rechts auf und ist damit intuitiv verständlich. Das kannst du
> direkt vorlesen, mach das mal in C.

Die Pascal-Vorgehensweise (die für jeden Pointer eine Typdeklaration 
verlangt, wenn ich mich richtig erinnere), kann man auch in C haben, 
wenn man denn unbedingt will:
1
        TYPE
2
            int_array_t = ARRAY[1..4] OF ARRAY[1..6] OF INTEGER;
3
            int_array_t_ptr = ^int_array_t;
4
5
        VAR
6
            ia : int_array_t;
7
            iap : int_array_t_ptr;
8
        BEGIN
9
            iap := ADDR(ia);
10
11
            iap^[2][2] := 2;
Man verzeihe mir, falls da was falsch dran wäre, aber es ist schon eine 
Weile her, seit ich die letzte Zeile Pascal geschrieben habe.

Das kann man auch in C 1:1 so hinschreiben:
1
        typedef int int_array_t[4][6];
2
        typedef int_array_t *int_array_t_ptr;
3
4
        int_array_t ia;
5
        int_array_t_ptr iap = &ia;
6
7
        (*iap)[1][1] = 2;
Das einzig "seltsame" daran (für einen Pascal-Kundigen) ist die 
Klammerung der Dereferenzierung, daran muß man sich halt gewöhnen, aber 
andere Sprachen haben teilweise auch "seltsame" Operator-Präzedenzen.

Ich bin jedenfalls ganz glücklich, daß es in C auch kürzer geht...

: Bearbeitet durch User
von 43rwedcfvt54esxcfgtredcvgffrdcvgfdxcvfd (Gast)


Lesenswert?

ich hab nicht alles gelesen ...

aber warum ein pointer auf den anfang?
um in der main damit rumzuspielen?

dann mach dir lieber eine funktion  und nimm die adresse von deinem 
&array[i][k]  als übergabeparameter
sonst fängst du xmal an hin und her zu rechnen nur weil du nicht an der 
richtne stelle im array bist

dabei wäre noch anzumerken ob ein 2dimensionales array eh das richtige 
ist für diesen zweck




pointer ...

wurde schon gesagt:  pointer sind alle gleich groß ( architekturbhängig 
)

wenn du aber den pointer incrementierst ( ptr++; )
dann  entscheidet der datentp wieviel incrementiert wird


deinen spass da oben kannst auch mit einem  ( void *ptr; ) machen ...
kann aber  bei ++ oder --  eben auch ins knie gehen

von (prx) A. K. (prx)


Lesenswert?

43rwedcfvt54esxcfgtredcvgffrdcvgfdxcvfd schrieb:
> wurde schon gesagt:  pointer sind alle gleich groß

Bevor daraus ein Dogma wird: Ich bin nicht sicher, ob das im Standard 
erzwungen ist, oder ob das heute bloss so üblich ist. Neben 
maschinenspezifischen Attributierungen für Adressräume stellt sich real 
die Frage, ob z.B. auf Harvard-Maschinen für Daten- und 
Funktions-Pointer evtl. unterschiedliche Grössen vorkommen dürften.

von kdjgkjktjjdühfbcnñcjc (Gast)


Lesenswert?

zumindest cortx m0 - 3 ist das so...

beim avr ist mir no kein unterscheid zu
uint8_t  oder void  aufgefallen

auch bei void (*foo)(void) nicht

von (prx) A. K. (prx)


Lesenswert?

Zwischen "kenne ich bei meiner Lieblingsarchitektur nicht anders" und 
"ist grundsätzlich immer so" gibts einen kleinen Unterschied.

AVR: char *p2;
     char __memx *p3;
     sizeof(p2) == 2
     sizeof(p3) == 3

Bei deinen ARMen gibts keinen Grund für unterschiedliche Grössen. Aber 
bei 16-bit x86 im medium memory model sind Datenpointer 2 Byte gross, 
Codepointer aber 4 Bytes. Wenngleich GCC/AVR das nicht so implementiert, 
wäre auch da bei entsprechend grossen AVRs ein grössere Codepointer 
denkbar.

: Bearbeitet durch User
von Bronco (Gast)


Lesenswert?

A. K. schrieb:
> Aber
> bei 16-bit x86 im medium memory model sind Datenpointer 2 Byte gross,
> Codepointer aber 4 Bytes.

Und beim C51 und C166 gab es doch noch diese "near", "far" und "huge" 
Pointer, die auch unterschiedlich groß waren. Ich erinnere mich dunkel 
an Limitierungen durch die Speichersegmentierung...

von Dirk B. (dirkb2)


Lesenswert?

Bronco schrieb:
> gab es doch noch diese "near", "far" und "huge"
> Pointer, die auch unterschiedlich groß waren.

Damals im RealMode unter MS-DOS gibt es das auch.

von Daniel A. (daniel-a)


Lesenswert?

Funktionspointer und gewöhnliche Pointer sind nunmal eine andere "Art" 
von Pointern. Jede Art von Pointern hat eine einheitliche grösse. Dinge 
wie __memx sind Spracherweiterungen, ich betrachte derartige Pointer als 
eine andere Art von Pointern. Wenn ich über Pointer ausserhalb des 
contexts von __memx und co. Spreche meine ich gewönliche standard c 
nichtfunktionspointer. Deshalb ist für mich die aussage "Alle Pointer 
sind gleich gross" wahr.

von 43rwedcfvt54esxcfgtredcvgffrdcvgfdxcvfd (Gast)


Lesenswert?

A. K. schrieb:
> Zwischen "kenne ich bei meiner Lieblingsarchitektur nicht anders" und
> "ist grundsätzlich immer so" gibts einen kleinen Unterschied.
>
> AVR: char *p2;
>      char __memx *p3;
>      sizeof(p2) == 2
>      sizeof(p3) == 3
>
> Bei deinen ARMen gibts keinen Grund für unterschiedliche Grössen. Aber
> bei 16-bit x86 im medium memory model sind Datenpointer 2 Byte gross,
> Codepointer aber 4 Bytes. Wenngleich GCC/AVR das nicht so implementiert,
> wäre auch da bei entsprechend grossen AVRs ein grössere Codepointer
> denkbar.


das mag sein
Ich hab nur den ARV GCC installiert und da ist mir kein unterschied 
aufgefallen.
zumal es hier glaube um einen AVR ging ...
und es nur ein einfacher zeiger im RAM war

es ging um uint8_t* , uint16_t* , uint32_t* ...


sonderfälle beim AVR sind die

__flash
    The __flash qualifier locates data in the .progmem.data section. 
Data is read using the LPM instruction. Pointers to this address space 
are 16 bits wide. These are 16-bit address spaces locating data in 
section .progmem.

__memx
    This is a 24-bit address space that linearizes flash and RAM: If the 
high bit of the address is set, data is read from RAM using the lower 
two bytes as RAM address. If the high bit of the address is clear, data 
is read from flash with RAMPZ set according to the high byte of the 
address.
Objects in this address space are located in .progmemx.data.




Ich selbst mache mir nur noch wenig gedanken um RAM ...
ich arbeite viel mit pointern.. fast schon zu viel ...
ich mag aber den zurgiff über -> ^^

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.