Forum: Compiler & IDEs Tabelle mit Zeiger auf Variablen


von J. V. (janvi)


Lesenswert?

brauch noch mal etwas Nachhilfe in C. Möchte eine Tabelle mit Zeigern 
auf Variablen machen, so daß ich auf diese in einer Schleife zugreifen 
kann obwohl sie der Linker irgendwo verstreut hat.
1
uint16_t Var1;
2
uint16_t Var2;
3
const uint16_t adrtab[] = {&Var1,&Var2} ;

meldet beim Compilieren:

arm-none-eabi-gcc -mcpu=cortex-m0plus -std=gnu11 -g3 -c -Os 
-DSTM32L011xx -DUSE_FULL_LL_DRIVER -mlittle-endian -Wall -mthumb 
-funsigned-char -mno-tpcs-frame -gdwarf-2 -oo/main.o main.c
main.c:159:28: warning: initialization of 'short unsigned int' from 
'uint16_t *' {aka 'short unsigned int *'} makes integer from pointer 
without a cast [-Wint-conversion]
  159 | const uint16_t adrtab[] = {&Var1,&Var2} ;       // Array of 
address table
      |                            ^
main.c:159:28: note: (near initialization for 'adrtab[0]')
main.c:159:28: error: initializer element is not computable at load time
main.c:159:28: note: (near initialization for 'adrtab[0]')
main.c:159:34: warning: initialization of 'short unsigned int' from 
'uint16_t *' {aka 'short unsigned int *'} makes integer from pointer 
without a cast [-Wint-conversion]
  159 | const uint16_t adrtab[] = {&Var1,&Var2} ;       // Array of 
address table
      |                                  ^
main.c:159:34: note: (near initialization for 'adrtab[1]')
main.c:159:34: error: initializer element is not computable at load time
main.c:159:34: note: (near initialization for 'adrtab[1]')
makefile:52: recipe for target 'o/main.o' failed
make: *** [o/main.o] Error 1

von Stephan (Gast)


Lesenswert?

J. V. schrieb:
> eine Tabelle mit Zeigern

Wieviele Bit haben denn Deine Zeiger ... ?

von A. S. (Gast)


Lesenswert?

J. V. schrieb:
> const uint16_t adrtab[] = {&Var1,&Var2} ;

"const uint16_t" ist kein Zeiger.

versuche "const uint16_t *"

von Falk B. (falk)


Lesenswert?

J. V. schrieb:
> brauch noch mal etwas Nachhilfe in C. Möchte eine Tabelle mit Zeigern
> auf Variablen machen, so daß ich auf diese in einer Schleife zugreifen
> kann obwohl sie der Linker irgendwo verstreut hat.

Versuchs mal so. Zeiger haben immer was mit dem * zu tun.
1
const uint16_t *adrtab[] = {&Var1,&Var2} ;

von Bastler_Hv (Gast)


Lesenswert?

const uint16_t adrtab[] = {&Var1,&Var2} ;

Du legst hier
const uint16_t adrtab[]
ein Array mit uint16_t an.

Du willst aber einen Zeiger auf uint16_t

von Janvi (Gast)


Lesenswert?

Ok, es ist ein Typproblem was ich gestern abend aufgrund der noch nie 
gesehenen Fehlermeldung nicht mehr geschnallt habe. Beim Variablentyp 
selbst muß ich noch mal etwas gründlicher nachschauen. Tatsächlich sind 
es vielerlei unterschiedliche Ram Variablen die beim Power-Fail 
Interrupt ins Eeprom gerettet werden sollen. Das möchte ich in einer 
Schleife über den sizeof Operator der Zeigertabelle machen. Es soll 
nicht jedesmal das Save und Restore nachgebessert werden wenn eine 
Variable entfällt oder neu erforderlich wird. Ein anderer Ansatz wäre 
über den Linker ein seperates .dataX Segment aufzumachen und sich dann 
die Anfangs und Endadressen zu holen. Aktuell gibt es aber noch genug 
Flash für ein Feld von Zeigern irgendwohin ins .bss.

Die Seitengröße des Eeproms ist 4 Bytes und es macht eigentlich keinen 
Sinn hier etwas systemunabhängig portabel machen zu wollen weil eben die 
Hardware so ist. Allerdings sind viele Variablen auch nur 2 oder 1 Byte 
weshalb ich da ein Union gemacht habe wo dann mehrere kleinere Variablen 
in einer Eeprom Seite zusammenfassen kann. Das (allseits knappe) Ram 
wird also ganz "normal" benutzt und Save und Restore kann dann auf den 4 
Byte Typ passend zur Seitengröße in der Schleife zugreifen. Hat sich 
eine Variable in der Seite geändert, wird zwangsweise die gesamte Seite 
gesichert.

const uint16_t *adrtab[] =  scheint schon mal korrekt zu compilieren 
weil jetzt klar ist daß es ein Feld von Zeigern sein soll. Ein 
Zeigereintrag in der Tabelle müsste voraussichtlich 4 Byte Flash belegen 
weil der Cortex M0+ ein 32 Bitter ist.

von Markus F. (mfro)


Lesenswert?

was hindert dich, das gleich richtig zu machen?

Anstatt alle Variablen mit einem Hilfskonstrukt einzusammeln, schreib' 
deinen Code um, so dass er diese Variablen direkt (in einer Struktur 
oder einem Array) hält. Dann kannst Du das in einem Rutsch wegschreiben 
oder laden.

von Janvi (Gast)


Lesenswert?

was hindert dich, das gleich richtig zu machen?

Das ist eben die Kunst des Programmierens und ich muß erst mal 
draufkommen was "richtig" ist. Oftmals bedarf es dazu eben einiger 
Iterationen und nicht umsonst ist "agil" in Mode.

von Klaus W. (mfgkw)


Lesenswert?

Wobei ich es nicht optimal finde, Variablen, die eigentlich nicht viel 
miteinander zu tun haben vermutlich, in ein Feld zu nötigen, nur weil 
sie als einzige Gemeinsamkeit das Etikett "soll ins EPROM kopiert 
werden" tragen.

Erstens weil der Quelltext weder lesbarer noch wartbarer wird, wenn man 
statt bspw. "zaehlerBenutzerzugriffe" etwas nichtssagendes wie 
"wichtigesArray[23]" nehmen muß.

Zum anderen bleibt es vielleicht nicht dabei, daß alle solchen Variablen 
in denselben Typ gezwängt werden können.

Deshalb ist ein Ansatz wie schon erwähnt mit einer eigenen 
Linker-section für alles, was so gesichert werden soll, wesentlich 
eleganter.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

J. V. schrieb:
> so daß ich auf diese in einer Schleife zugreifen
> kann obwohl sie der Linker irgendwo verstreut hat

Und du erwartest jetzt das der Compiler schon weiß wo das später sein 
wird?

von Janvi (Gast)


Lesenswert?

Es gibt dabei leider noch ein anderes Problem: Neben Ram Variablen sind 
auch einige Register aus der Hardware wie etwa Zählerstände von 
Inkrmentalgebern zu sichern und wiederherzustellen. Eine Zeigertabelle 
kann da locker draufzeigen und wenn man dort schreiben und lesen kann 
funktioniert alles wie auch im Ram.  Um die Hardware-Register in den 
Struct reinzukriegen müssten man sie dazu zunächst jeweils gesondertes 
Umkopieren. Also vielleicht doch wieder zurück auf Anfang...

von Janvi (Gast)


Lesenswert?

> Und du erwartest jetzt das der Compiler schon weiß wo das später sein
wird?

Vom Linker möchte ich das schon erwarten daß er selbst weis wohin er die 
Variablen legt.

von Adam P. (adamap)


Lesenswert?

Und wenn du das einfach abstrahierst?

Also:
Falls der Zustand eintrifft, dass alles weggeschrieben werden muss,
rufst du eine Fkt. auf "save_critical_data(...)".
In dieser hast du z.B. eine Struktur die dein Inhalt vom EEPROM 
abbildet.
Diese wird mit get() Funktionen auf alle einzelnen Variablen/Register 
befüllt. Da es ja sein kann, dass du sonst kein Zugriff auf einige 
Variablen hast.

Dann wärst fertig damit.

Ein Ort wo alles zusammen kommt und du brauchst keine Zeiger die 
irgendwo hin zeigen und es ist klar strukturiert.

Oder passt dieser Ansatz bei dir nicht?

von Janvi (Gast)


Lesenswert?

>Diese wird mit get() Funktionen auf
>alle einzelnen Variablen/Register befüllt.

Dann kann ich sie ja gleich ins Eeprom schreiben anstelle zuvor noch in 
ein Struct. Ausserdem müsste ich das Befüllen und Wiederherstellen in 
beiden Richtungen pflegen und deshalb wollte ich das so nicht. Außerdem 
ist RAM immer knapp weshalb man das nicht doppelt will (auch nicht auf 
Stack/Heap). Das umkopieren mit get() oder sonstwas genügt auch für die 
wenigen HW Register welcher ausser der Reihe liegen.

von Adam P. (adamap)


Lesenswert?

Janvi schrieb:
> Dann kann ich sie ja gleich ins Eeprom schreiben

Wenn du das einzeln machst, brauchst du mehrere Zugriffe (dauert 
länger).

Janvi schrieb:
> Außerdem
> ist RAM immer knapp weshalb man das nicht doppelt will

Aber Zeiger nehmen je nach System manchmal sogar mehr Platz ein als der 
Wert ansich, je nach Variablentyp.

von Janvi (Gast)


Lesenswert?

Jede Seite muß sowieso einzeln bzw. in 4 Byte Portionen geschrieben 
werden sobald ein Bit anders ist. Dauert also genau gleich lang.

Nachdem der Cortex M0+ ein 32 bitter ist, sind es die Zeiger wohl auch 
und ebenso die Seitengröße mit den Werten. Es steht also 1:1 aber der 
Unterschied ist, daß die Werte im Ram stehen und die Zeiger ins Flash 
können wovon es ungleich mehr gibt.

Momentan gibt es noch die Schwierigkeit, daß die Zähler-Register 16 bit 
sind und die anderen 2 Byte der Eeprom Seite dann ungenutzt bleiben wenn 
ich das in der Schleife mache. Aus Sicht des Wear-Leveling halbiert das 
die Lebensdauer weil ich eine weitere Seite für das nächste 16 bit 
Register brauche. Also müsste ich pro Seite zwei Zeiger spendieren um 
alles zusammenklauben zu können. Die Zeiger würden dann immerhin doppelt 
so viel Flash Bytes belegen wie der angezeigte Inhalt im Ram bzw. IO 
Bereich.

von Adam P. (adamap)


Lesenswert?

Janvi schrieb:
> Nachdem der Cortex M0+ ein 32 bitter ist, sind es die Zeiger wohl auch
> und ebenso die Seitengröße mit den Werten. Es steht also 1:1 aber der
> Unterschied ist, daß die Werte im Ram stehen und die Zeiger ins Flash
> können wovon es ungleich mehr gibt.

Sorry, das klingt irgendwie alles sehr verwirrend.
Wenn es ein 32Bit ist, dann ist da gar nix 1:1.

Wenn du 4x 16Bit Variablen im RAM hast, nehmen die 64Bit RAM ein.
Wenn du nun 4x Zeiger auf diese Variablen erzeugst, nehmen diese Zeiger
4x 32Bit ein = 128Bit.

Somit nehmen die Zeiger dir viel mehr RAM weg, als wenn du dir eine 
Kopie dieser Variablen erstellst.

Und wenn du eine Kopie hast, z.B. in einer struct und du bei dieser noch 
das alignment abstellst, dann kannst du die so ins eeprom schreiben und 
dabei bleiben auch keine Bytes leer (frei).

: Bearbeitet durch User
von Gunther M. (gunther_m)


Lesenswert?

J. V. schrieb:

[...]
>
1
> uint16_t Var1;
2
> uint16_t Var2;
3
> const uint16_t adrtab[] = {&Var1,&Var2} ;
4
>
[...]

1. Wurde schon geschrieben: Du brauchst kein Feld von uint16_t sondern 
ein
Feld von Zeigern auf uint16_t
2. Die Priorität von * (Zeiger) ist höher als [] (Feld).
Also:

const uint16_t *(adrtab[]) = {&Var1,&Var2};

adrtab ist ein Feld von Zeigern auf uint16_t. Spiralenregel.
Da gibt es so eine Webseite "taking c gibberisch to english" oder so.
Weiss den Namen nicht mehr.

Gruß
Gunther

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.