Forum: Mikrocontroller und Digitale Elektronik Hilfe die Zeiger kommen !


von Andreas S. (define_andreas)


Angehängte Dateien:

Lesenswert?

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.

von nicht"Gast" (Gast)


Lesenswert?

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,

von test (Gast)


Lesenswert?

Hast du dich schon mit Zeigerarithmetik und Zeigerdreferenzierung 
beschäftigt?

von Michael .. (gismi)


Lesenswert?

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.

von Andreas S. (define_andreas)


Lesenswert?

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;
};

von Andreas S. (define_andreas)


Angehängte Dateien:

Lesenswert?

Hallo,

vollständigkeitshalber der gesamte Header !

von Point (Gast)


Lesenswert?

Zeiger sind immer wieder ein Spass auch nach Jahren, aber vielleicht 
hilft das hier:

http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/012_c_zeiger_001.htm#mjee28a882ef9125d2c4cc49121b2c65c1

Da sind eigentlich alle Infos drin die du suchst... Hoffe es hilft dir!

Schau dir auch mal die "main.h", da ist die Datenstruktur "struct 
eeprom_giess" definiert auf den der Zeiger gpEEPROM nachher 
wahrscheinlich zeigen soll.

von Andreas S. (define_andreas)


Lesenswert?

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

von Nop (Gast)


Lesenswert?

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.

von Andreas S. (define_andreas)


Lesenswert?

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 
?

von Point (Gast)


Lesenswert?

1
      
2
if(save)
3
{
4
        eeprom_write_block(&flower, &gpEEPROM[index], sizeof(struct eeprom_giess));
5
        print_flower_setup(index, &flower);
6
}

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?

von Victor Vector (Gast)


Lesenswert?

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

von Andreas S. (define_andreas)


Lesenswert?

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 ?

von test (Gast)


Lesenswert?

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.

von Andreas S. (define_andreas)


Lesenswert?

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

von Andreas S. (define_andreas)


Lesenswert?

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 ?

von test (Gast)


Lesenswert?

Andreas S. schrieb:
> das stimmt, aber gpEEPROM beinhaltet doch schon eine Adresse ??

Das hier
1
&gpEEPROM[i].pump_on

musst Du dir so denken
1
&(gpEEPROM[i].pump_on)

neben Zeigerarithmetik, Zeigerdereferenzierung musst Du dich auch 
unbedingt noch mit Operatorenprioritäten beschäftigen.

von Andreas S. (define_andreas)


Lesenswert?

Victor Vector schrieb:
> int *ptr_myvar = &myvar; //deklarieren und initialisieren in einem mit
> Variable myvar

... Initialisierung mit der Adresse von myvar - oder ?

von Andreas S. (define_andreas)


Lesenswert?

test schrieb:
> musst Du dir so denken
> &(gpEEPROM[i].pump_on)

jetzt wird es heller :-)

von Andreas S. (define_andreas)


Lesenswert?

zwischendurch vielen Dank für eure Unterstützung !!

von Victor Vector (Gast)


Lesenswert?

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 !

von Nop (Gast)


Lesenswert?

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.

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

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

von Dirk B. (dirkb2)


Lesenswert?

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 *

von Nop (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Andreas S. (define_andreas)


Lesenswert?

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

von Nop (Gast)


Lesenswert?

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)

von Dirk B. (dirkb2)


Lesenswert?

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
1
char c feld[20];
2
int  i;
3
4
// aus 
5
c = feld[i];
6
// macht der Compiler
7
c = *(feld+i);
8
9
//Da dies auch dasselbe ist wie 
10
c = *(i+feld);
11
//kann man auch
12
c = i[feld]; // schreiben.
13
14
// Es geht auch sowas
15
c = "abcdef"[2];
16
// da ein Stringliteral auch nur eine Adresse ist

von Michael .. (gismi)


Lesenswert?

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?

von Nop (Gast)


Lesenswert?

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

von Nop (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Andreas S. (define_andreas)


Lesenswert?

Nop schrieb:
> &((*(gpEEPROM + i)).pump_on)

Michael .. schrieb:
> *(gpEEPROM +sizeof(eeprom_giess)*i)

hilft beides zu meinem Verständnis !

Vielen Dank nochmals an Alle !!!

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.