Warnung bei volatile (AVR-GCC)

OP #421865
Lesenswert?

Hallo,

kann mir jemand auf die Sprünge helfen? Habe im Forum nach "volatile"
gesucht, aber über die folgende Warnung hat noch niemand berichtet
(vielleicht ist der Grund auch trivial??).

In einer RTC-Funktion wird ein Zeit/Datum-Array "TimeDate" erhöht;
die RTC-Funktion wird von einem Timer-Interrupt aufgerufen (im
Minutentakt, entsprechend heruntergeteilt). Früher hatte ich das
"volatile" bei "TimeDate"-Deklaration vergessen, da war noch alles
okay, hat auch  funktioniert (vielleicht auch nur "zufällig" - okay,
bei Computern gibt es keine Zufälle).

Dann erkannte ich das Versäumnis, daß Variablen, die in
Interrupt-Funktionen verändert werden, als volatile deklariert werden
müssen. Habe dann das "volatile" hinuzgefügt; das Programm
funktioniert natürlich immer noch, aber nun erscheinen Warnungen bei
den "memcpy"-Funktionen (gibt noch mehr, als im Code-Fragment
steht):

../main.c:1936: warning: passing arg 2 of `memcpy' discards qualifiers
from pointer target type


Code-Fragment:

#define TIMEDATENUMBYTES 5   /* Zahl der Bytes der TimeDate-Arrays */

volatile uint8_t TimeDate[TIMEDATENUMBYTES]; /* hh:mm dd:mm:yy */

...


void ZeitDatumAnzeigen(void)
{
  uint8_t timedate[TIMEDATENUMBYTES]; /* lokale TimeDate-Variable */

  ...

  sreg=SREG;     /* Globale Zeit lokal kopieren */
  cli();
  memcpy(timedate,TimeDate,sizeof(timedate));
  SREG=sreg;

  fprintf_P(&DevLCD,PSTR("%02u:%02u   %02u.%02u.%02u"),
     timedate[0],timedate[1],timedate[2],timedate[3],timedate[4]);

  ...
}


Wahrscheinlich erscheinen die Warnungen im Zusammenhang mit der
Verwendung von Zeigern ("TimeDate" ist ja ein Zeiger) und der
Deklaration von "memcpy", das ja nicht mit "volatile" deklariert
ist.

Die Warnung stört mich, obwohl alles funktioniert. Weiß jemand, wie man
sie wegbekommen kann?

Danke.

Günter
Gast #421867
Lesenswert?

Du bist da in einer Zwickmühle.

Die Warnung besagt logisch gedacht, dass das volatile von memcpy()
nicht beachtet wird. Und das ist eine gute Warnung.

memcpy() nimmt beim Kopieren keine Rücksicht darauf, dass sich der
Inhalt der Quelle während der Kopieraktion ändern kann. memcpy setzt
sogar voraus, dass sich die Quelle nicht ändert (Deklaration mit const
void *).

Du berücksichtigst das Verhalten von memcpy() bereits, indem du mit
cli() die Interrupts sperrst, so dass sich der Inhalt von TimeDate
nicht mehr ändern kann.

Um die Warnung an dieser Stelle wegzubekommen, finde ich es legitim
einen cast einzufügen.
Moderator Persönliche Seite #421868
Lesenswert?

Wenn man allerdings die Warnung erfolgreich mit dem Cast los wird
und der Code danach trotzdem noch funktioniert, ist das eigentlich
das beste Zeichen, dass es in diesem Falle des "volatile"s gar
nicht bedurft hätte.

> memcpy setzt sogar voraus, dass sich die Quelle nicht ändert
> (Deklaration mit const void *).

Nein, diese Deklaration besagt, dass es selbst das Objekt, auf das
der Zeiger zeigt, nicht ändern möchte.
#422550
Lesenswert?

> Dann erkannte ich das Versäumnis, daß Variablen, die in
> Interrupt-Funktionen verändert werden, als volatile deklariert werden
> müssen.

Und auch das stimmt so nicht.
Von müssen kann keine Rede sein.

Das volatile ist im Grunde nur für den wichtig, der diese Variable auch
benutzt. Wie zb hier:

   while( j == 5 )
     ;

ist j nicht volatile deklariert, so geht der Compiler davon aus, dass
j sich in der Schleife nicht ändern kann und optimiert entsprechend.
Eine volatile Deklaration hingegen sagt dem Compiler: "Du weist auch
nicht alles über diese Variable. Die Variable kann sich ändern, ohne 
dass
es in dem Code-Stück, das du überblicken kannst, ersichtlich wäre."

Das macht volatile und das hat erstmal nichts mit Interrupts an sich
zu tun.
Gast #422568
Lesenswert?

@Jörg: "Wenn man allerdings die Warnung erfolgreich mit dem Cast los 
wird
und der Code danach trotzdem noch funktioniert, ist das eigentlich
das beste Zeichen, dass es in diesem Falle des "volatile"s gar
nicht bedurft hätte."

Dieser Logik folgend wird man, wenn man mit eher einfach gestrickten 
Compilern arbeitet, nie volatile benötigen. Und sich fuchsteufelswild 
über einen völlig unbrauchbaren GCC ärgern, der das halt etwas anders 
sieht.

Nein, so läuft das nicht. Array-Zugriffe werden in der Praxis oft kein 
volatile benötigen. Aber irgendwann vielleicht schon. Also muss es sein, 
auch dann wenn sich erst einmal nichts ändert.
Moderator Persönliche Seite #422573
Lesenswert?

> Array-Zugriffe werden in der Praxis oft kein
> volatile benötigen. Aber irgendwann vielleicht schon.

Nein, brauchen sie nicht.  Das ist nur die Kurzfassung, für die
Formulierung der Langfassung, warum sie (*) kein "volatile" brauchen,
würde ich vermutlich eine halbe Stunde benötigen, um die
entsprechenden Stellen des Standards herauszusuchen und zu zitieren.

Wenn man das Beschreiben eines Arrays zwischen zwei Threads (also
z. B. zwischen Interrupt- und Hauptkontext) synchronisieren will,
sollte man das über eine (dann wirklich "volatile" qualifizierte)
Semaphore-Variable machen.

Du kannst dir ziemlich sicher sein, dass der GCC an dieser Stelle
bereits alles ausnutzt, was der Standard gestattet.

(*) In der Regel -- du kannst natürlich pathologische Fälle
provozieren, wenn du sowas hast wie
1
uint8_t myarr[42];
2

3
...
4

5
   while (myarr[23] != 5)
6
     /* wait until it's 5 */ ;
OP #422582
Lesenswert?

Danke an alle - Problem ist gelöst mit dem "(void *)" (hätte ich selber 
drauf kommen können, aber manchmal hat man ja Tomaten auf den Augen).

Danke auch für den Hinweis von Jörg - ja, man kann hier "volatile" 
bestimmt weglassen; aber der Klarheit wegen lasse ich's doch drin (es 
wird nicht schaden).

Günter

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren