Forum: Compiler & IDEs warning: cast to pointer from integer of different size


von Timo P. (latissimo)


Lesenswert?

Hallo!


Folgender Minimalcode liefert die Warning. Warum, weiß ich nicht. Ich 
habe extra einen typecast gemacht.

1
#include <avr/io.h>
2
#include <avr/eeprom.h>
3
4
int main()
5
6
{
7
  for(unsigned char i = 0; i<100;i++)
8
    eeprom_write_byte((unsigned char*)i,0x22);
9
10
  return 0;
11
}

Danke für Erklärungen!

von (prx) A. K. (prx)


Lesenswert?

Mit
  for(unsigned i = 0; i<100;i++)
    eeprom_write_byte((unsigned char*)i,0x22);
ist Ruhe.

Was dir der Compiler damit sagen wollte:
  (unsigned char) hat 1 Byte, (unsigned char *) hat 2
und das ist zwar nicht falsch, aber könnte ein Hinweis auf ein 
Missverständnis sein.

von Ralf (Gast)


Lesenswert?

A. K. schrieb:
> Was dir der Compiler damit sagen wollte:
>   (unsigned char) hat 1 Byte, (unsigned char *) hat 2
> und das ist zwar nicht falsch, aber könnte ein Hinweis auf ein
> Missverständnis sein.
Ich kann es jetzt nicht testen, aber die Warnung müsste auch kommen, 
wenn für 'i' z.B. uint16_t (32, 64) verwendet wird. Das Entscheidende: 
'i' ist eine normale Variable. Da die Funktion eine Adresse erwartet, 
wird gemeckert. Einfach eine Zahl als Zeiger verwenden, kann ordentlich 
in die Hose gehen. Denn: woher weißt du denn, dass an den Adressen 0..99 
nichts steht? Hier vielleicht zufällig... Deshalb die Warnung. Typecast 
lieber vermeiden!
Man könnte das so machen, dann gibt's (zumindest vom Compiler) 
hoffentlich nichts zu meckern:
1
#define E_MAX 100
2
uint8_t EEMEM Ziel[E_MAX];     // Speicher im EPROM ordentlich reservieren
3
for (uint8_t i=0; i < E_MAX; i++)
4
 eeprom_write_byte(&Ziel[i],0x22);
(Für dieses spezielle Problem gibt's natürlich 'eeprom_write_block')

von Verwirrter Anfänger (Gast)


Lesenswert?

Ralf schrieb:
> Denn: woher weißt du denn, dass an den Adressen 0..99
> nichts steht?

Grundsätzlich kann ich aber doch davon ausgehen, dass ich der einzige 
bin, der dahin schreibt, oder?

D.h. Wenn ich nirgendwo eine Variable mit EEMEM deklariert habe, und 
keine meiner libraries den EEPROM beschreibt, sollte ich mich beliebig 
austoben dürfen. Richtig?

Oder optimiert der gcc soweit, dass er Sachen ins EEPROM auslagert?

von Ralf (Gast)


Lesenswert?

Verwirrter Anfänger schrieb:
> Grundsätzlich kann ich aber doch davon ausgehen, dass ich der einzige
> bin, der dahin schreibt, oder?
Das ist schon richtig, aber nicht die feine Art! Der Compiler hat schon 
seinen Grund, rumzumäkeln. Mit dem muss man sich nicht unbedingt 
anlegen. Und wenn du hier schon, sagen wir mal, schlampig mit den 
Datentypen umgehst, dann gibt's woanders mit Sicherheit böse 
Überraschungen!

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Ich kann es jetzt nicht testen, aber die Warnung müsste auch kommen,
> wenn für 'i' z.B. uint16_t (32, 64) verwendet wird.

Ich hätte gedacht, der Wortlaut der Meldung des Compilers sei deutlich 
genug. Bei unverträglichen Typen klingt das anders und ist auch keine 
Warnung mehr, sondern ein Fehler.

> Das Entscheidende:
> 'i' ist eine normale Variable. Da die Funktion eine Adresse erwartet,
> wird gemeckert.

Drum hatte er dort ja auch den type cast verwendet. Mit dem wird der 
type check ausgehebelt und pointer-integer Konvertierungen sind legal.

von Ralf (Gast)


Lesenswert?

Hab' gerade mal meinen Bastelcomputer hochgefahren.
Und
Timo P. schrieb:
> for(unsigned char i = 0; i<100;i++)
>     eeprom_write_byte((unsigned char*)i,0x22);
geändert zu
1
for(uint16_t i = 0; i<100;i++)
2
 eeprom_write_byte((uint8_t*)i,0x22);
... hätte nicht gedacht, dass der Compiler so eine Schlamperei 
durchgehen lässt.

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> ... hätte nicht gedacht, dass der Compiler so eine Schlamperei
> durchgehen lässt.

Das ist nicht die eleganteste Methode, EEPROM Speicher zu allozieren, 
aber was die Sprache C angeht ist das vollkommen in Ordnung. Und muss 
auch von jedem Compiler akzeptiert werden.

Wenn dir sowas schon gegen die Hutschnur geht, dann schau dir lieber 
nicht an, was sich hinter Makros wie PORTC verbirgt. Bei deiner 
Philosophie würde man bei Controller-Programmierung in Warnungen 
regelrecht ersaufen.

von Ralf (Gast)


Lesenswert?

Ich habe noch schnell mal so ein paar 'Konstruktionen' durchprobiert 
(C++ Builder). Ich hätte gedacht, dass da wenigstens so ein klizekleines 
Warnungchen kommen müsste. Aber richtig, es gibt ja immer mal wieder 
spezielle Bibliotheksfunktionen, da werden nur 'int'-s übergeben und das 
sind dann auch manchmal Adressen.
Aber trotzdem (ich hoffe es war nicht ernst gemeint):
Verwirrter Anfänger schrieb:
> Wenn ich nirgendwo eine Variable mit EEMEM deklariert habe, und
> keine meiner libraries den EEPROM beschreibt, sollte ich mich beliebig
> austoben dürfen.

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Aber trotzdem (ich hoffe es war nicht ernst gemeint):
> Verwirrter Anfänger schrieb:

>> Wenn ich nirgendwo eine Variable mit EEMEM deklariert habe, und
>> keine meiner libraries den EEPROM beschreibt, sollte ich mich beliebig
>> austoben dürfen.

Womit er Recht hat. Man kann das EEPOM auch als Bytestrom vergleichbar 
zu einem Diskfile betrachten. Also keine einzelnen Variablen mit mehr 
oder weniger fester Position. Habe ich auch schon gemacht. Dann sieht 
das in der Implementierung ungefähr so aus wie hier.

von Ralf (Gast)


Lesenswert?

A. K. schrieb:
> Dann sieht das in der Implementierung ungefähr so aus wie hier.
Mmh, könnte ich mir vorstellen, aber irgendwo sollte schon mal 
deklariert werden, dass vom EPROM was verwendet wird. Also ich meine, 
nicht rumtoben.

von willibald (Gast)


Lesenswert?

Meiner Meinung nach ist das API falsch gewählt:
1
void eeprom_write_byte (uint8_t *addr, uint8_t value);
Was hier addr heißt, ist in Wahrheit kein Zeiger, sondern ein Offset. 
Dieser Sachverhalt sollte sich im API widerspiegeln:
1
void eeprom_write_byte (uint16_t addr, uint8_t value);
wäre daher meiner Meinung nach besser.

von Vlad T. (vlad_tepesch)


Lesenswert?

A. K. schrieb:
> Mit
>   for(unsigned i = 0; i<100;i++)
>     eeprom_write_byte((unsigned char*)i,0x22);
> ist Ruhe.

Ralf schrieb:
> geändert zu
> for(uint16_t i = 0; i<100;i++)
>  eeprom_write_byte((uint8_t*)i,0x22);

warum schreibt man nicht das hin, was man meint:
1
for(uint8_t* i = 0; i<100;i++)
2
  eeprom_write_byte(i,0x22);

von Vlad T. (vlad_tepesch)


Lesenswert?

willibald schrieb:
> Was hier addr heißt, ist in Wahrheit kein Zeiger, sondern ein Offset.

nein, es ist ein Zeiger in den eeprom-Adressbereich, der auch mit 0 
anfängt.

willibald schrieb:
> Dieser Sachverhalt sollte sich im API widerspiegeln:
> void eeprom_write_byte (uint16_t addr, uint8_t value);
> wäre daher meiner Meinung nach besser.

wenn schon, dann
void eeprom_write_byte (uint16_t offset, uint8_t value);

von (prx) A. K. (prx)


Lesenswert?

Vlad Tepesch schrieb:

> warum schreibt man nicht das hin, was man meint:
1
for(uint8_t* i = 0; i<100;i++)
2
  eeprom_write_byte(i,0x22);
Weil man diesen Code vom Compiler um die Ohren gehauen bekommt (i<100).

von Rolf Magnus (Gast)


Lesenswert?

willibald schrieb:
> Meiner Meinung nach ist das API falsch gewählt:void eeprom_write_byte (uint8_t
> *addr, uint8_t value);
> Was hier addr heißt, ist in Wahrheit kein Zeiger, sondern ein Offset.

Offset zu was? Es ist die Adresse einer Speicherstelle, also das, was 
eben auch in einem Zeiger steht.

> Dieser Sachverhalt sollte sich im API widerspiegeln:void eeprom_write_byte 
(uint16_t addr, uint8_t value);
> wäre daher meiner Meinung nach besser.

Wenn man dann eine EEMEM-Variable definiert, müßte man deren Adresse 
immer erst in einen Integer konvertieren, um sie an die Funktion zu 
übergeben.

von willibald (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> nein, es ist ein Zeiger in den eeprom-Adressbereich, der auch mit 0
> anfängt.

Zeigertypen sind dann sinnvoll, wenn es etwas gibt, was man damit 
dereferenzieren kann. Das ist hier nicht der Fall, darum wäre es besser, 
den Adress-Offset als Integer anzugeben.

Mir fällt auch keine sinnvolle Anwendung ein, die an dieser Stelle 
tatsächlich einen Pointer braucht. Wisst ihr eine? Alle Szenarien, die 
mir einfallen, führen entweder auf definierte Offsets oder auf 
Pointer-Differenzen (also auch wieder Ganzzahlen).

von (prx) A. K. (prx)


Lesenswert?

willibald schrieb:

> Zeigertypen sind dann sinnvoll, wenn es etwas gibt, was man damit
> dereferenzieren kann. Das ist hier nicht der Fall, darum wäre es besser,
> den Adress-Offset als Integer anzugeben.

Im Flash auch? Das ist in GCC ebensowenig dereferenzierbar. Die Leute 
würden sich über die vielen dann notwendigen Casts sicherlich wenig 
freuen. Und beides verschieden zu definieren wäre grotesk.

> Wisst ihr eine?

Ganz normale EEPROM Variablen.

von Rolf Magnus (Gast)


Lesenswert?

willibald schrieb:
> Mir fällt auch keine sinnvolle Anwendung ein, die an dieser Stelle
> tatsächlich einen Pointer braucht. Wisst ihr eine?

Siehe http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#EEPROM

> Alle Szenarien, die mir einfallen, führen entweder auf definierte Offsets oder 
auf
> Pointer-Differenzen (also auch wieder Ganzzahlen).

Nochmals: Differenzen zu was?

von willibald (Gast)


Lesenswert?

OK, dieser EEMEM-Ansatz war mir nicht bekannt. Liegt vermutlich daran, 
dass ich kein AVR-Anwender bin.

Aber ihr habt Recht, das Konzept ist stimmig. Wieder was gelernt.

von (prx) A. K. (prx)


Lesenswert?

Bei den Xmegas liegt das EEPROM zudem im Datenadressraum und ist 
folglich dereferenzierbar.

von Karl H. (kbuchegg)


Lesenswert?

Ralf schrieb:

> ... hätte nicht gedacht, dass der Compiler so eine Schlamperei
> durchgehen lässt.


Was erwartest du?
Mit dem Cast sagst du dem Compiler explizit: Halt die Klappe.

von Ralf (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was erwartest du?
> Mit dem Cast sagst du dem Compiler explizit: Halt die Klappe.
Jaa, jaa. ;-)

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.