Liebe Profis,
als "Zeiger Lehrling :-)" brauche ich dringend Unterstützung !
In dem gegenständlichen Beispiel wird am Anfang ein Zeiger deklariert
und mit dem Wert 0 initialisiert.
struct eeprom_giess *gpEEPROM = (struct eeprom_giess*)0;
Das heißt doch, dass gpEEPROM ein Zeiger ist und eine Adresse beinhaltet
welche auf die erste Stelle im EEProm verweist.
Daher ist mir auch klar, dass in der Routine void get_flower_name ...
nur gpEEPROM ohne "&" weitergegeben wird, da es sich ja schon um eine
Adresse handelt.
Aber warum wurde mit der oben angeführten Definition auch ein Array
deklariert ? gpEEPROM[index]
und warum wird in der Routine void print_csv das "&" verwendet wenn es
sich doch schon um einen Zeiger handelt.
Hm,
ohne die Definition des Structes zu kennen ist das ein wenig
Rätselraten.
Andreas S. schrieb:> Das heißt doch, dass gpEEPROM ein Zeiger ist und eine Adresse beinhaltet> welche auf die erste Stelle im EEProm verweist.
Nicht so ganz, in der Struct steht noch ein wenig mehr als nur die
Adresse im Eeprom. Warscheinlich ist das Eeprom mit Daten des Structs
vollgeknallt und der Autor greift einfach hart auf die Adressen zu.
Das ist nicht schön, funktioniert aber unter gewissen Bedingungen.
Wie gesagt, ohne Definition des Structs...
Grüße,
Andreas S. schrieb:> Aber warum wurde mit der oben angeführten Definition auch ein Array> deklariert ? gpEEPROM[index]
Das ist im Code keine Deklaration, sondern ein Zugriff.
Such dir ein C Tutorial für Pointer. Das wird dich bei C ewig
beschäftigen und zum verzweifeln bringen, wenn du die Grundlagen nicht
verstanden hast.
test schrieb:> Hast du dich schon mit Zeigerarithmetik und Zeigerdreferenzierung> beschäftigt?
und
Michael .. schrieb:> Such dir ein C Tutorial für Pointer
und wie ...
ich hatte auch geglaubt es so halbwegs verstanden zu haben. Aber dieses
Beispiel ????
nicht"Gast" schrieb:> Wie gesagt, ohne Definition des Structs...
struct eeprom_giess
{
unsigned char automatik;
uint16_t pump_on;
uint16_t pump_off;
char name[16];
uint16_t seconds_on;
uint16_t hours_off;
};
Point schrieb:> Schau dir auch mal die "main.h", da ist die Datenstruktur "struct> eeprom_giess" definiert
... eben nicht !!!
"main.h" das ist die vorhin eingestellte Headerdatei "header.c" und
darin ist die Struktur nur deklariert ?
Definitionen zu dieser Struktur finden sich nur im Hauptprogramm und
haben keinen Bezug zu gpEEPROM ???
Ich danke dir für den Link.
LG
Andreas S. schrieb:> Aber warum wurde mit der oben angeführten Definition auch ein Array> deklariert ? gpEEPROM[index]
Weil in C Pointer und Arrays eng verwandt sind, wenn auch nicht
identisch (bei einem Array wird Speicher für die Nutzdaten reserviert,
bei einem Pointer nicht). Aber beim Verwenden kann man einen Zeiger
genauso wie ein Array nutzen; man muß nur sichergestellt haben, daß
irgendwo der Speicher alloziert wurde.
Sieht man übrigens oft, wenn man einen Pointer hat und den Speicher dann
per malloc reserviert - und dann der Zeiger wie ein Array genutzt wird.
Oder auch, wenn man ein Array an eine Funktion übergibt - das verflacht
dann zu einem Zeiger, weswegen man die Längeninformation dabei
normalerweise als zusätzliche Variable mitgibt, wenn man das braucht.
> und warum wird in der Routine void print_csv das "&" verwendet wenn es> sich doch schon um einen Zeiger handelt.
Weil dort wird erst der Zeiger dereferentiert wird, dann auc das struct
geguckt und die Adresse von einer spezifischen Variablen rangeholt wird.
&gpEEPROM[i].pump_on im Einzelnen erklärt:
- nimm den Zeiger gpEEPROM
- addiere i-mal die Größe seines Datentyps drauf (d.h. gehe i von diesen
structs weiter)
- bei dem struct, wo man dann ist, nimm die Variable pump_on
- und besorg Dir mit dem & davon jetzt die Adresse.
Völlig übliches C-Idiom.
Nop schrieb:> man muß nur sichergestellt haben, daß> irgendwo der Speicher alloziert wurde.
das muss aber anscheinend bei einem EEProm nicht zwingend geschehen,
bzw. ist das in diesem Fall nicht gemacht worden ?
Nop schrieb:> Weil dort wird erst der Zeiger dereferentiert wird,
das heißt "&" bedeutet in diesem Fall Dereferentierung und neue Adresse
?
hier wird doch der Speicherinhalt beginnend an der Adresse "&flower" +
sizeof(struct eeprom_giess) (Typ struct eeprom_giess) aufs Eeprom
beginnend an der Speicherstelle "&gpEEPROM[index]" + sizeof(struct
eeprom_giess) geschrieben. Da iss doch der Zusammenhang!? Oder meinst du
was anderes?
Die Funktion eeprom_write_block schreibt bestimmt einfach stumpf an die
Stelle im Eeprom die angegeben ist mit der Länge sizeof(struct
eeprom_giess).
Hast du das gemeint?
int myvar = 0; // die normale Variable brauchen wir später
1.) Deklaration und Initialisierung
int *ptr_myvar; // Zeiger vom Typ int deklarieren (zeigt noch auf nix)
ODER
int *ptr_myvar = (int *)0x0100; // deklarieren und initialisieren in
einem an Adresse 0x0100
ODER
int *ptr_myvar = &myvar; //deklarieren und initialisieren in einem mit
Variable myvar
2.) in der Anwendung
ptr_myvar = &myvar; // Zeiger ptr_myvar ADRESSE von myvar zuweisen
*ptr_myvar = myvar; // Zeiger ptr_myvar WERT von myvar zuweisen
*ptr_myvar = &myvar; // Zeiger ptr_myvar ADRESSE von myvar als Wert
zuweisen
ptr_myvar = myvar; // Zeiger ptr_myvar WERT von myvar als ADRESSE
zuweisen
Nop schrieb:> Autor:>> Nop (Gast)
nach kurzer Überlegung noch eine Frage:
Wie unterscheidet der Compiler bei '&' zwischen
1) Dereferentierung und neue Adresse
bzw.
2) Adresse des Zeigers ?
Andreas S. schrieb:> das heißt "&" bedeutet in diesem Fall Dereferentierung und neue Adresse> ?
die funktion 'eeprom_read_block' braucht vermutlich als zweiten
Parameter eine Adresse. Das '&' ist hier ein Adressoperator.
test schrieb:> die funktion 'eeprom_read_block' braucht vermutlich als zweiten> Parameter eine Adresse.
das stimmt, aber gpEEPROM beinhaltet doch schon eine Adresse ??
Das ist ja genau der Punkt den ich so schwer verstehe :-(
struct eeprom_giess *gpEEPROM = (struct eeprom_giess*)0;
struct eeprom_giess *gpEEPROM - Zeiger deklarieren
(struct eeprom_giess*)0; dezimal 0 in Adresse umwandeln
Victor Vector schrieb:> *ptr_myvar = &myvar; // Zeiger ptr_myvar ADRESSE von myvar als Wert> zuweisen
darüber muss ich noch nachdenken ...
Was bedeutet das, wenn in einem Zeiger ein Wert steht ?
Victor Vector schrieb:> int *ptr_myvar = &myvar; //deklarieren und initialisieren in einem mit> Variable myvar
... Initialisierung mit der Adresse von myvar - oder ?
ptr_myvar = &myvar; // Zeiger ptr_myvar ADRESSE von myvar zuweisen
*ptr_myvar = myvar; // Zeiger ptr_myvar WERT von myvar zuweisen
Diese beiden Beispiele werden häufig gebraucht.
*ptr_myvar = &myvar; // Zeiger ptr_myvar ADRESSE von myvar als Wert
zuweisen
ptr_myvar = myvar; // Zeiger ptr_myvar WERT von myvar als ADRESSE
zuweisen
Diese beiden Beispiele sind SYNTAKTISCH korrekt, ergeben aber fast nie
einen Sinn im Programmkontext.
---------------------------------
var repräsentiert den Wert der Variablen
&var repräsentiert die Adresse der Variablen
ptr_var repräsentiert die Adresse des Zeigers
*ptr_var repräsentiert den Wert, auf den der Zeiger zeigt
*var und &ptr_var sind beide syntaktisch falsch
---------------------------------
Anwendungsbeispiel:
int var[] = {0,1,2,3} // array mit 4 Werten
int *ptr_var = var[0]; // ptr_var zeigt jetzt auf den ersten Wert im
array
ptr_var++; // ptr_var zeigt jetzt auf den 2. Wert im array
ptr_var++; // ptr_var zeigt jetzt auf den 3. Wert im array
Obwohl ++ immer um 1 erhöht ist springt der Zeiger bei ++ um 4 Byte, da
er vom Typ int ist. Der Zeiger "weiss" um sein Datenformat (eigendlich
weiss es der Compiler). Bei Funktionsaufrufen kann man das nutzen, um
nur einen Zeiger auf ein komplexes Datenformat als Parameter zu
übergeben statt viele Einzelparameter (z.B. ein struct).
Aber nichts ohne Ausnahme:
void *ptr_var ist ein Zeiger ohne Datenformat, der auf ein beliebiges
Format gecastet werden kann. Aber nichts überstürzen !
Victor Vector schrieb:> *var und &ptr_var sind beide syntaktisch falsch
nein &ptr_var ist nicht falsch, das ist die Adresse des Zeigers. Kann
man beispielsweise gebrauchen, wenn man den Zeiger by reference
übergibt, etwa für eine verlinkte Liste, wenn die ohne
Dummy-Head-Element realisiert ist.
übersetze einfach
*<irgendwas> = "Zeiger_auf"<irgendwas>
&<irgendwas> = "Adresse_von"<irgendwas>
und dann verinnerlichen, dass jedes Datenobjekt irgendwo im Speicher
abgelegt wird.
Jedes Datenobjekt halt also eine Adresse, wo es im Speicher liegt. Und
diese Adresse kann man halt erreichen mit &<irgendwas>.
und wer schaut auf dieses Datenobjekt? Natürlich ein Zeiger. Also zeigt
ein Zeiger mit *<irgendwas> genau auf das Datenobjekt, welches an der
Adresse &<irgendas> abgelegt ist.
Nop schrieb:> Weil in C Pointer und Arrays eng verwandt sind, wenn auch nicht> identisch (bei einem Array wird Speicher für die Nutzdaten reserviert,> bei einem Pointer nicht).
Beim Array gibt es keinen (von C zugreifbaren) Ort, an dem die Adresse
abgespeichert ist.
> Aber beim Verwenden kann man einen Zeiger> genauso wie ein Array nutzen;
Vorsicht.
Der Arrayname ergibt die Adresse vom ersten Element des Arrays. Mehr
nicht.
Mit dieser Adresse kannst du dann arbeiten.
Du kannst einem Array keinen neuen Bereich zuweisen, demnach sind Größe
und Ort fest, Post/Pre-Inkrtement geht auch nicht.
Der Adressoperator & ergibt unterschiedliche Werte und Typen und auch
sizeof() liefert andere Werte.
Der Compiler macht aus einem Arrayzugriff mit [] erstmal eine
Dereferenzierung mit *
Dirk B. schrieb:> Du kannst einem Array keinen neuen Bereich zuweisen, demnach sind Größe> und Ort fest, Post/Pre-Inkrtement geht auch nicht.
Ja sicher, das folgt ja alles daraus, daß bei einem Array auch Speicher
alloziert wird, bei einem Pointer nicht. Das mit Inkrement ist
allerdings ein weiterer Gesichtspunkt, ja.
Man kann aber z.B. auch einen Pointer mit den eckigen Klammern wie ein
Array nutzen, die Klammern sind syntaktischer Zucker.
Dirk B. schrieb:> Der Adressoperator & ergibt unterschiedliche Werte und Typen
Gut, ich hab auch noch nie bei einem Array den Adreßoperator benutzt.
> und auch sizeof() liefert andere Werte.
Ja, weil bei einem Array der Speicher alloziert wird und der Arrayname
nur ein Alias ist, während bei einem Pointer die Größe eines Zeigers auf
der jeweiligen Plattform zurückkommt.
Nop schrieb:> Man kann aber z.B. auch einen Pointer mit den eckigen Klammern wie ein> Array nutzen, die Klammern sind syntaktischer Zucker.
Wieso auch? Das hört sich so besonders an.
Du kannst eine Adresse (ob vom Pointer oder Arraynamen) dereferenziern.
Das kannst du mit dem * Operator oder (als Zucker) mit den [] machen.
Nop schrieb:> Gut, ich hab auch noch nie bei einem Array den Adreßoperator benutzt.
Ich auch nicht, aber schau mal in Anfängerforen beim Einlesn von
C-Strings nach.
"Wieso ist das überflüssig, es geht doch!" liest man dann da.
Liebe Profis,
vielen, vielen Dank für eure Hilfe. Ihr habt mir sehr geholfen und ich
kann leider nicht behaupten - ich hätte auch selber drauf kommen können
:-)
Eine ergänzende Frage hätte ich noch:
Nop schrieb:> Man kann aber z.B. auch einen Pointer mit den eckigen Klammern wie ein> Array nutzen, die Klammern sind syntaktischer Zucker.
Das war für mich eben unverständlich woher das Zeigerarray kommt welches
gar nicht definiert wurde.
eeprom_read_block(&schwelle, &gpEEPROM[i].pump_on, sizeof(schwelle));
im Detail: &gpEEPROM[i].pump_on übersetzt dank eurer Hilfe zu
&(gpEEPROM[i].pump_on) soweit war das dann klar für mich mit Ausnahme
der [] Klammer,
was aber vermutlich nur bedeutet:
&((gpEEPROM * i).pump_on - hoffe ich jedenfalls :-)
Andreas S. schrieb:> &(gpEEPROM[i].pump_on) soweit war das dann klar für mich mit Ausnahme> der [] Klammer,> was aber vermutlich nur bedeutet:> &((gpEEPROM * i).pump_on - hoffe ich jedenfalls
Nee, das nicht. Der Ausdruck "gpEEPROM[i]" ist dasselbe wie *(gpEEPROM +
i). Man kann daher "&(gpEEPROM[i].pump_on)" ohne eckige Klammern auch so
schreiben:
&((*(gpEEPROM + i)).pump_on)
oder so:
&((gpEEPROM + i)->pump_on)
Nop schrieb:
> Man kann aber z.B. auch einen Pointer mit den eckigen Klammern wie ein> Array nutzen, die Klammern sind syntaktischer Zucker.
Das geht sogar noch weiter
Dirk B. schrieb:> Das geht sogar noch weiter
Ja, immer wieder gern gesehen bei obfuscated code contests. :-)
Michael .. schrieb:> der richtigkeitshalber müsste es nicht eher so sein?
Nee, Pointerarithmetik addiert automatisch die Größe des Datentyps dazu
und nicht Bytes. Beispiel:
Bei "char *p1" mit der Annahme p1==8 ergäbe p1+1 dann 9.
Bei uint32_t *p2 mit der Annahme p2==8 ergäbe p2+1 dann 12.
Das mit dem sizeof brauchste bei malloc, etwa um auf einen
uint32-t-Pointer 5 uint32_t zu allozieren:
uint32_t *p3; p3 = malloc(5 * sizeof(uint32_t));
Nop schrieb:> uint32_t *p3; p3 = malloc(5 * sizeof(uint32_t));
Ach ja, und im Unterschied dazu dann noch calloc:
p3 = calloc(5, sizeof(uint32_t));
Das ist schon ein hirnrissiger Teil der C-Standardbibliothek, daß man
gleichartige Funktionen mit verschiedener API realisiert hat.
Michael .. schrieb:> Nop schrieb:>> Der Ausdruck "gpEEPROM[i]" ist dasselbe wie *(gpEEPROM +i)>> der richtigkeitshalber müsste es nicht eher so sein?>
1
*(gpEEPROM+sizeof(eeprom_giess)*i)
> oder stehe ich gerade auf dem Schlauch?
Bei der Arrayschreibweise gesht du ja auch davon aus, dass immer die
richtige Anzal Bytes weiter gezählt wird.
Und aus
Dirk B. schrieb:> // aus> c = feld[i];> // macht der Compiler> c = *(feld+i);
folgt dann, dass dies auch bei der normalen Zeigerarithmetik so ist.