Forum: Compiler & IDEs strtok discards volatile qualifier


von R. B. (britzi)


Lesenswert?

Hallo zusammen,

ich habe da mal wieder eine kleine Frage ;-)

Ich zerlege Strings mit dem Befehl strtok, um sie danach mit anderen 
Strings vergleichen zu können.


So sieht das in meinem Code aus:
1
volatile char uart_string0[];
2
char *pointer;
3
pointer = strtok (uart_string0, delimiter1);



Beim Compilieren bekomme ich dann eine Warnung:

passing argument 1 of 'strtok' discards 'volatile' qualifier from 
pointer target type [enabled by default]

Was genau ist das Problem und wie löse ich es?
Ich habe schon danach gesucht, aber keine Lösung gefunden und für dieses 
Problem reichen meine C-Kenntnisse leider noch nicht.

Bisher funktioniert es, aber ich möchte die Warnung gerne beseitigen.

Danke :-)

: Verschoben durch User
von Klaus W. (mfgkw)


Lesenswert?

Das Probem ist halt, daß du einen Zeiger auf volatile char übergibst, 
strok() aber einen Zeiger auf nicht-volatile char erwartet.

Die Warnung alleine beseitigen kannst du, indem du das Argument an 
strtok castest, sodaß das volatile seitens der Funktion verschwindet.

Generell ist es aber keine gute Idee, einfach mit cast eine Warnung zu 
umgehen - die Warnung hat manchmal Sinn.
Deshalb: wieso ist dein Feld volatile? Kann sich tatsächlich zur 
Laufzeit etwas darin ändern? Falls ja, ist es kein geeignetes Argument 
für strtok.
Falls nein, ist das volatile überflüssig.

: Bearbeitet durch User
von R. B. (britzi)


Lesenswert?

Danke,

die Variable ist volatile, weil das der String ist, der den Puffer für 
UART darstellt.

Ich verwende das UART Beispiel für den Interruptempfang aus dem 
GCC-Tutorial:
1
#define UART_MAXSTRLEN 10
2
 
3
volatile uint8_t uart_str_complete = 0;     // 1 .. String komplett empfangen
4
volatile uint8_t uart_str_count = 0;
5
volatile char uart_string[UART_MAXSTRLEN + 1] = "";
6
7
ISR(USART_RXC_vect)
8
{
9
  unsigned char nextChar;
10
 
11
  // Daten aus dem Puffer lesen
12
  nextChar = UDR;
13
  if( uart_str_complete == 0 ) {  // wenn uart_string gerade in Verwendung, neues Zeichen verwerfen
14
 
15
    // Daten werden erst in uart_string geschrieben, wenn nicht String-Ende/max Zeichenlänge erreicht ist/string gerade verarbeitet wird
16
    if( nextChar != '\n' &&
17
        nextChar != '\r' &&
18
        uart_str_count < UART_MAXSTRLEN ) {
19
      uart_string[uart_str_count] = nextChar;
20
      uart_str_count++;
21
    }
22
    else {
23
      uart_string[uart_str_count] = '\0';
24
      uart_str_count = 0;
25
      uart_str_complete = 1;
26
    }
27
  }
28
}


Wie kann ich nun das Problem lösen, so dass es ordentlich gelöst ist?

Danke :-)

von Bitflüsterer (Gast)


Lesenswert?

Nun ja. Volatile ist nützlich, kann aber auch Gehirnrindenverbiegung mit 
sich bringen.

Ich komme gleich auf dein konkretes Problem, möchte aber die dringende 
Empfehlung vorausschicken, sich im Internet und Büchern nochmal 
ausführlich über das Thema volatile zu informieren.

Nun aber:
Kurz und bündig verhindert volatile, das der Compiler bei Optimierungen 
annimmt, das der schon mal z.B. in ein Register aus einer Variablen 
geladene Wert nach einigen anderen Schritten immer noch gültig ist.

In Deinem Code wird aber genau genommen, durch die Variable 
uart_str_complete verhindert, dass sich die Werte in dem Buffer 
unerwartet verändern.

Daraus folgt, dass, solange Du das Flag uart_str_complete wirklich 
konsequent verwendest, ein volatile des Buffers nicht notwendig ist.
Andererseits könntest Du in diesem Fall tatsächlich das volatile in dem 
Aufruf von strok weg-casten, sprich: durch explizite Typumwandlung 
entfernen, ohne das es zu negativen Effekten kommt.
Erstere Lösung ist aber vorzuziehen, weil es klar besser ist, volatile 
wegzulassen, wenn es nicht nötig ist.

Ganz schlaue übrigens, wozu ich z.B. Peter Danegger zähle, machen es 
ganz anders. Sie machen eine explizite Typumwandlung in volatile an 
den Stellen der Zugriffe. Aber das nur nebenbei.

Ist das soweit klar, oder bleiben noch Fragen offen?

von R. B. (britzi)


Lesenswert?

Vielen Dank für die ausführliche Antwort.

Das Flag wird konsequent verwendet.
Ich habe das volatile weggestrichen und nun funktioniert es wie es soll.

Nun sehe ich auch, dass an dieser Stelle das volatile eigentlich 
überflüssig ist. Ich dachte nur, dass es schon so seine Richtigkeit 
haben wird, wenn es hier im Tutorial steht.


Ich hätte noch eine ganz andere Frage, wo ich jetzt gerade die 
Spezialisten hier habe. ;-)


Gibt es eine Möglichkeit bei einem Connect durch einen PC ohne das 
dieser nachfragt, eine Statusmeldung einmalig abzugeben?

Sprich ich klicke im Terminalprogramm auf Connect und nachdem die 
Verbindung hergestellt ist, kommt sofort ohne Nachfrage "Hallo ich bin 
xy" oder so ähnlich.

Danke :-)

von Klaus W. (mfgkw)


Lesenswert?

Wenn du RTS/CTS machst, geht das - der Einfachheit halber lässt man das 
bei Controllern aber gehrne weg und nimmt nur 3 Drähte.

Was man aber bei den meisten Terminalprogrammen machen kann, ist daß sie 
beim Aufbau erstmal irgendwas schicken (gedacht, um an ein Modem 
irgendwelche AT...-Kommandos zu schicken.
Das könntest du im Controller abfragen, und dann deine Statusmeldung 
schicken.
Das würde dann auch mit drei Leitungen ohne irgendwelche Steuerleitungen 
gehen.

von R. B. (britzi)


Lesenswert?

Vielen Dank :-)

Ich denke RTS/CTS ist mir zu kompliziert.
Dafür sehe ich auch nicht so wirklich die Notwendigleit.
Ich überprüfe den Empfang bei mir einfach durch Rückmeldungen und bleibe 
bei den drei Leitungen.

Wenn sich das von Terminalprogramm zu Terminalprogramm unterscheidet, 
lasse ich das lieber gleich. Dann gibts eben kein Hallo Welt zu Beginn 
;)

von Oliver S. (oliverso)


Lesenswert?

R. B. schrieb:
> Wie kann ich nun das Problem lösen, so dass es ordentlich gelöst ist?

Wiel viele Strings direkt nacheinander erwartest du denn über die 
Schnittstelle?
Bei deiner Lösung kann die UART nichts empfangen, solange die 
Strngbearbeitung noch nicht fertig ist.

Dagenen hilft ein zweiter Empfangsbuffer, in den der komplette String 
nach empfang hineinkopiert wird. Über Flags zwischen ISR und 
Hauptprogramm synchronisiert, brauchen beide Puffer nicht volatile zu 
sein.

Damit kann die UART schon den nächsten String empfangen, während du den 
ersten bearbeitest. Der Nachteil ist der zusätzliche Speicherbedarf für 
den zweiten Buffer. Ob das noch passt, und ob du das überhaupt 
benötigtst, musst du selber rausfinden.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver S. schrieb:

> Dagenen hilft ein zweiter Empfangsbuffer, in den der komplette String
> nach empfang hineinkopiert wird. Über Flags zwischen ISR und
> Hauptprogramm synchronisiert, brauchen beide Puffer nicht volatile zu
> sein.

Das stimmt nicht ganz, denn ob volatile oder nicht, hängt nicht davon 
ob, ob irgend eine andere Variable volatile ist und als 'Zugriffsschutz' 
fungiert. Der Compiler kann deswegen immer nocht entscheiden, dass er 
die fragliche Variable in einem Register hält. Obwohl das bei einem 
Array natürlich recht unwahrscheinlich ist. So viele Register hat der 
Compiler nicht zur Verfügung.

Unabhängig davon ist natürlich die Betrachtungsweise für ...
> Damit kann die UART schon den nächsten String empfangen,

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.