Forum: Mikrocontroller und Digitale Elektronik Zeiger auf String in Flash - warning incompatible pointer type


von Matthias S. (dachs)


Angehängte Dateien:

Lesenswert?

Es geht um die Anbindung eines LCD mit 4x27 Zeichen und 2 Controllern 
der HD44780-Familie.

Ich habe keine Idee mehr, warum diese Warnung erscheint, wenn ich die 
Funktion mit
1
void lcd_write_string_p(uint8_t, uint8_t, char *x);
in demo_lcd.h deklariere.
1
+ avr-gcc -save-temps -c -Os -mmcu=atxmega64a3u demo_lcd.c demo_lcd_test.c
2
demo_lcd_test.c: In function ‘main’:
3
demo_lcd_test.c:55:1: warning: passing argument 3 of ‘lcd_write_string_p’ from incompatible pointer type [enabled by default]
4
 lcd_write_string_p(0,0,&titel_0);
5
 ^
6
In file included from demo_lcd_test.c:9:0:
7
./demo_lcd.h:9:6: note: expected ‘char *’ but argument is of type ‘__flash const __flash char (*)[12]’
8
 void lcd_write_string_p(uint8_t, uint8_t, char *x);
9
      ^
10
demo_lcd_test.c:56:1: warning: passing argument 3 of ‘lcd_write_string_p’ from incompatible pointer type [enabled by default]
11
 lcd_write_string_p(3,0,&titel_3);
12
 ^
13
In file included from demo_lcd_test.c:9:0:
14
./demo_lcd.h:9:6: note: expected ‘char *’ but argument is of type ‘__flash const __flash char (*)[12]’
15
 void lcd_write_string_p(uint8_t, uint8_t, char *x);
16
      ^
17
+ avr-gcc -mmcu=atxmega64a3u demo_lcd.o demo_lcd_test.o --output demo_lcd_test.elf
18
+ avr-objcopy -O ihex -R .eeprom demo_lcd_test.elf demo_lcd_test.hex

Deklariere ich diese nicht, gibt es keine Warnung.
Das fertige Hex (mit demo_lcd_test.sh uebersetzt) tut in beiden Faellen, 
was es soll.
Hat jemand von den C-Gurus eine Erklaerung?

Danke Matthias

von Georg G. (df2au)


Lesenswert?

Matthias S. schrieb:
> expected ‘char *’ but argument is of type ‘__flash const __flash char
> (*)[12]’

Dein Compiler schreibt dir doch alles. Wo ist das Problem?

von Theor (Gast)


Lesenswert?

Was soll man da viel erklären? Das Problem gibt der Compiler ja aus.

Der ursprüngliche Autor hat vermutlich die Warnungen entweder abgestellt 
oder ignoriert.

von Georg G. (df2au)


Lesenswert?

Nein, der LCD Treiber wurde für eine ältere GCC Version geschrieben, die 
den Qualifier "flash" noch nicht kannte (oder der Autor kannte ihn 
nicht).

von Theor (Gast)


Lesenswert?

@ df2au

Ich versteh gerade nicht, worauf sich das "Nein" bezieht.

Im Democode wird ja auch der __flash qualifier verwendet.

Aber noch etwas:
Es wird mit "&titel_0" ein Zeiger auf ein Array mit bestimmter Länge 
übergeben. Die Funktion will aber nicht so etwas, sondern einen Zeiger 
auf char.

von Matthias S. (dachs)


Lesenswert?

Theor schrieb:
> Es wird mit "&titel_0" ein Zeiger auf ein Array mit bestimmter Länge
> übergeben. Die Funktion will aber nicht so etwas, sondern einen Zeiger
> auf char.

Meine Absicht war, einen Zeiger auf den Anfang des 0-terminierten 
Strings zu uebergeben. Das klappt ja auch, der 0-terminierte String wird 
bis vor die "\0" auf das Display geschrieben.

Was mir unerklaerlich ist: einfaches Auskommentieren der Zeile:
1
void lcd_write_string_p(uint8_t, uint8_t, char *x);
in demo_lcd.h reicht, um alle Warnungen verschwinden zu lassen, obwohl 
da m.M.n. exakt dasselbe ablaeuft, warum?

Matthias

von Carl D. (jcw2)


Lesenswert?

Matthias S. schrieb:
> Theor schrieb:
>> Es wird mit "&titel_0" ein Zeiger auf ein Array mit bestimmter Länge
>> übergeben. Die Funktion will aber nicht so etwas, sondern einen Zeiger
>> auf char.
>
> Meine Absicht war, einen Zeiger auf den Anfang des 0-terminierten
> Strings zu uebergeben. Das klappt ja auch, der 0-terminierte String wird
> bis vor die "\0" auf das Display geschrieben.
>
> Was mir unerklaerlich ist: einfaches Auskommentieren der Zeile:
>
1
void lcd_write_string_p(uint8_t, uint8_t, char *x);
> in demo_lcd.h reicht, um alle Warnungen verschwinden zu lassen, obwohl
> da m.M.n. exakt dasselbe ablaeuft, warum?
>
> Matthias

Verschwinden die Warnungen oder funktioniert es auch?
Das Problem ist: ein char* zeigt ins RAM, ein __flash char* zeigt ins 
Flash. Bein AVRs braucht man zum Zugriff unterschiedliche Befehle. Und 
wenn der Compiler keine Deklaration einer Funktion hat, dann glaubt er 
dem Aufrufer.

von Georg G. (df2au)


Lesenswert?

Carl D. schrieb:
> Das Problem ist: ein char* zeigt ins RAM, ein __flash char* zeigt ins
> Flash.

In demo-lcd wird ein char * als Übergabe Parameter vereinbart.
void lcd_write_string_p(uint8_t, uint8_t, char *x);

In der Schreibroutine wird dann per pgm_read auf das Flash zugegriffen. 
Damit wird das Pointer Ziel automatisch korrigiert.
void lcd_write_string_p(uint8_t line, uint8_t column, const char *s){
[...]
while(c = pgm_read_byte(s)){s++; lcd_write_byte_top(c);};

Hier würde der Compiler maulen, wenn das .h File auch im .c mit 
eingebunden würde. Denn die Definition aus .h stimmt nicht mit der 
Realisierug im .c überein (char *   <->  const char *).


in demo-lcd-test kommt dann aber die Definition
const __flash char titel_0[] = "Time      \0";
wobei __flash automatisch const ist.

Als Abhilfe könnte man z.B. durchgängig __flash verwenden, muss dann 
aber die pgm_read weglassen. Das macht der Compiler selbst richtig. Also
void lcd_write_string_p(uint8_t, uint8_t, __flash char *x);
void lcd_write_string_p(uint8_t line, uint8_t column, __flash char *s){
while(c = *s)){s++; lcd_write_byte_top(c);};


Fazit: Der Compiler ist schlauer, als man denkt :-)

von Matthias S. (dachs)


Lesenswert?

Georg G. schrieb:
> Als Abhilfe könnte man z.B. durchgängig __flash verwenden, muss dann
> aber die pgm_read weglassen. Das macht der Compiler selbst richtig. Also
> void lcd_write_string_p(uint8_t, uint8_t, __flash char *x);
> void lcd_write_string_p(uint8_t line, uint8_t column, __flash char *s){
> while(c = *s)){s++; lcd_write_byte_top(c);};
>
Ja, das klappt.

> Fazit: Der Compiler ist schlauer, als man denkt :-)
Sowieso.

Allerdings war der Schluessel das, was ich letztes Jahr schon selbst 
geschrieben hatte:

Matthias S. schrieb:
> Meine Absicht war, einen Zeiger auf den Anfang des 0-terminierten
> Strings zu uebergeben.

Also so:
1
lcd_write_string_p(0,0,&titel_0[0]);
Das zeigt auf das erste Byte und nicht falsch wie hier:
1
lcd_write_string_p(0,0,&titel_0);
Das zeigte auf den String.

Schoenes neues Jahr noch und Vielen Dank.

Matthias

von Georg G. (df2au)


Lesenswert?

Matthias S. schrieb:
> Also so:lcd_write_string_p(0,0,&titel_0[0]);Das zeigt auf das erste Byte
> und nicht falsch wie hier:lcd_write_string_p(0,0,&titel_0);

Das wäre kürzer und macht das Richtige.
lcd_write_string_p(0,0,titel_0);

von W.S. (Gast)


Lesenswert?

Matthias S. schrieb:
>> Fazit: Der Compiler ist schlauer, als man denkt :-)
> Sowieso.

Sowieso? Der einzige Fehler, den ich hier sehe besteht darin, daß du 
viel zu wenig von der Hardware kennst, insbesondere nicht den 
Unterschied zwischen Harvard und v.Neumann-Architekturen.

Zum Programieren von µC gehört eben zuvörderst eine ordentliche Kenntnis 
der Hardware. Nicht alles, was in gewöhnlichem C syntaktisch richtig 
ist, darf man hier als gegeben ansehen. Und wenn dann der Compiler einem 
im Klartext sagt, was man falsch gemacht hat, dann sollte man das auch 
sorgfältig lesen und verstehen.

Du bist hier nicht der Einzige, dem das passiert. Viele Leute hier 
glauben, wenn sie nur eine tolle C-Quelle von irgendwo her haben, sich 
nicht weiter um die HW scheren zu müssen. Stattdessen wird dann mit 
neumodischen Tinnef-Bezeichnern herumgefummelt und man glaubt, damit 
über alle erdenklichen Systeme hinweg portabel zu sein. Ist aber nicht 
so.

W.S.

von Matthias S. (dachs)


Lesenswert?

W.S. schrieb:
> daß du
> viel zu wenig von der Hardware kennst, insbesondere nicht den
> Unterschied zwischen Harvard und v.Neumann-Architekturen.

Nein. Auf Hardwareebene weiss ich sehr genau, was da passiert und wie 
das laufen soll.
Wie ich das dem C-Compiler so beibringe, dass der nicht meckert, ist 
eine andere Sache und genau um die ging es hier.

W.S. schrieb:
> Nicht alles, was in gewöhnlichem C syntaktisch richtig
> ist, darf man hier als gegeben ansehen.

Nochmal, es ging darum, dass der Compiler eben nicht der Meinung war, 
das sei syntaktisch richtig - obwohl er das Richtige daraus kompiliert 
hat, es hat ja trotz der Warnungen funktioniert, wie ich schon im 
Eingangspost geschrieben hatte.

Georg G. schrieb:
> Das wäre kürzer und macht das Richtige.
> lcd_write_string_p(0,0,titel_0);

Ja, das klappt auch.

Matthias

Edit: Typos.

von Georg G. (df2au)


Lesenswert?

Matthias S. schrieb:
> der Compiler eben nicht der Meinung war, das sei syntaktisch richtig

Die Warnung sagt eher: "Bist du dir sicher, dass du das so haben willst? 
Über diese Konstruktion kann man durchaus geteilter Meinung sein." Die 
Syntaxprüfung in C ist meist recht lasch, man kann sich selbst von 
hinten erschiessen. Gute Compiler geben vorher wenigstens eine Warnung. 
Viele Programmierer schalten die Warnungen auf niedrige Level oder ganz 
ab, weil sie nerven. So gesehen, warst du auf der richtigen Seite.

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.