Hallo,
ich möchte einen Pointer erstellen der mir auf die Adresse eines Arrays
zeigt.
1
#include<avr/io.h>
2
3
volatileuint8_tdata[5][6];
4
volatileuint16_t*data_pointer=&data;
5
6
intmain(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
@ A.K. Funktioniert leider auch nicht.
Der Compiler (AtmelStudio) meldet immer noch:
initialization from incompatible pointer type [enabled by default]
Chris
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.
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.
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.
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.
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".
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...
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.
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?
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.
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:
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.
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.
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.
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_tdata[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
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.
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
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?
... 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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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
structX
2
{
3
uint32_tb;
4
uint32_tc;
5
uint32_tfoo[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?
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.
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.
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
TYPEptr=^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?
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.
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 *)
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:
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*)
DJ Tobsen schrieb:> Jetzt bin ich aber verwirrt!>> Ich hätte jetzt gedacht, das hier>
1
>volatileuint8_t(*data_pointer)[6];
2
>
> ist doch ein Array aus 6 Pointern, oder?
Das hier
1
volatileuint8_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
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;
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)
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
typedefintint_array_t[4][6];
2
typedefint_array_t*int_array_t_ptr;
3
4
int_array_tia;
5
int_array_t_ptriap=&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...
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
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.
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.
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...
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.
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.
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 -> ^^