Forum: Compiler & IDEs Wo ist der Unterschied: volatile uint8_t oder uint8_t volatile ?


von Jens N. (midibrain)


Lesenswert?

Hallo,

habe bei mir im C Programm beide Varianten:

volatile uint8_t und uint8_t volatile

Scheint beides zu funktionieren. Macht der Compiler einen Unterschied?

Jens

: Bearbeitet durch User
von Foobar (asdfasd)


Lesenswert?

Nein, ist das gleiche (wie auch bei den anderen type specifiers const 
and restrict).  Allerdings musst du bei komplexeren Typen aufpassen: 
"volatile char *" ist "char volatile *" und nicht "char *volatile" oder 
gar "char volatile *volatile".

von MaWin O. (mawin_original)


Lesenswert?

Ist das gleiche. Google: Qualifier.

Beitrag #7575984 wurde vom Autor gelöscht.
von Isabella Z. (isabella_z)


Lesenswert?

Der Unterschied zwischen volatile uint8_t und uint8_t volatile in C 
liegt in der Position des Schlüsselworts volatile in der Deklaration. In 
beiden Fällen wird die Variable als volatile deklariert, was bedeutet, 
dass der Wert der Variablen sich jederzeit ändern kann, und somit der 
Compiler keine Annahmen über den Wert der Variablen machen sollte. Dies 
ist besonders wichtig bei Hardware-naher Programmierung, wie zum 
Beispiel bei der Arbeit mit Mikrocontrollern.

Bei volatile uint8_t steht das Schlüsselwort volatile vor dem Typ 
(uint8_t). Bei uint8_t volatile steht es nach dem Typ. In der Praxis 
macht diese unterschiedliche Positionierung des volatile-Schlüsselworts 
jedoch keinen Unterschied in der Funktionsweise oder Bedeutung der 
Variable. Beide Deklarationen geben an, dass der Wert der Variablen 
extern beeinflusst werden kann und deshalb bei jedem Zugriff neu aus dem 
Speicher gelesen werden sollte.

______________________________________________________________________ 
__
https://ichfrage.com/frage/1229/wie-kann-ich-meine-computerperformance-verbessern#answer-1229

von Oliver S. (oliverso)


Lesenswert?

Isabella Z. schrieb:
> Der Unterschied zwischen ...

Das ist ja noch schlimmer als ChatGPT. Wer oder was produziert solch 
einen geistigen Dünnschiß?

Oliver

von Wf88 (wf88)


Lesenswert?

Isabella Z. schrieb:
> ______________________________________________________________________
> __
> 
https://ichfrage.com/frage/1229/wie-kann-ich-meine-computerperformance-verbessern#answer-1229

Was ist denn das für ein Klick-Fänger-Scheisse?!

Ich dachte das sei die Quelle für deine Antwort und habe drauf geklickt. 
Verdammt. Hätte mir am Titel im Link schon auffallen müssen...

von M. N. (bmbl2)


Lesenswert?

Foobar schrieb:
> Nein, ist das gleiche (wie auch bei den anderen type specifiers
> const
> and restrict).  Allerdings musst du bei komplexeren Typen aufpassen:
> "volatile char *" ist "char volatile *" und nicht "char *volatile" oder
> gar "char volatile *volatile".

Richtig. Konkret beziehen sich alle Type-Qualifier (volatile, const, 
unsigned, signed) VOR dem Stern auf die "pointed-at" Variable.

Alle Type Qualifier nach dem Stern beziehen sich auf den Pointer selbst.
1
volatile char my_char_var;
2
volatile char * const foo = &my_char_var;
foo ist in diesem Fall also ein const pointer auf einen volatile char.

Man muss jetzt nur noch die storage qualifier im Kopf haben:
1
static volatile char * const foo = &my_char_var;
Das ist KEIN const pointer auf einen static volatile char, sondern ein 
statisch-allokierter const pointer auf ein volatile char. Das static 
(Storage qualifier) bezieht  sich logischerweise immer auf den pointer, 
obwohl es vor dem Sternchen steht.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

M. N. schrieb:
> Richtig. Konkret beziehen sich alle Type-Qualifier (volatile, const,
> unsigned, signed) VOR dem Stern auf die "pointed-at" Variable.

unsigned und signed sind keine type-qualifiers, sondern type-specifiers 
und haben eigene Regeln.

> Alle Type Qualifier nach dem Stern beziehen sich auf den Pointer selbst.

Einfach gesagt: type-qualifiers beziehen sich immer auf das, was 
unmittelbar links davon steht, außer wenn sie selbst ganz links stehen. 
Dann beziehen sie sich auf das, was unmittelbar rechts davon steht.

> Man muss jetzt nur noch die storage qualifier im Kopf haben:

Genau genommen heißen sie storage-class-specifier, und sie beziehen sich 
immer auf das Ganze.

von Peter D. (peda)


Lesenswert?

volatile ist veraltet und sollte durch die atomic Typen bzw. Macros 
ersetzt werden.
Der Zweck von volatile war, daß andere Instanzen die Variable auch 
zugreifen können und sie deshalb nicht temporär in Registern gehalten 
werden darf. Damit können jedoch Konflikte auftreten, wenn die Zugriffe 
nicht atomar erfolgen. Z.B. für read-modify-write Zugriffe (foo++) ist 
das ein Problem. Erst mit der atomic Kapselung ist garantiert, daß jede 
Instanz gültige Variablen sieht.
Da atomic implizit auch volatile bedeutet, wird volatile nicht mehr 
benötigt und ist überflüssig.

von Jens N. (midibrain)


Lesenswert?

Hallo Peter,
wie wird das dann geschrieben:

atomic uint8_t ?

Jens

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Einfach gesagt: type-qualifiers beziehen sich immer auf das, was
> unmittelbar links davon steht, außer wenn sie selbst ganz links stehen.
> Dann beziehen sie sich auf das, was unmittelbar rechts davon steht.

Das ist eine Gruppe von Wörtern, die man in jeder Reihenfolge 
hinschreiben kann: storage-class-specifier, type-specifier und 
type-qualifier. Alles vor dem x kann man beliebig umsortieren:
1
 unsigned volatile int const short extern x;

Man kann sich also wie du Eselsbrücken bauen, muss aber im Kopf 
behalten, dass es innerhalb dieser Gruppe keine echten Regeln gibt.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Und jetzt wiederhole die Übung mal für einen Pointer ...

von Oliver S. (oliverso)


Lesenswert?

(prx) A. K. schrieb:
> Alles vor dem x kann man beliebig umsortieren:1 unsigned
> volatile int const short extern x;

Alles vor dem x steht ja auch links davon, und macht daher eh kein 
Verständnisproblem.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter D. schrieb:
> volatile ist veraltet und sollte durch die atomic Typen bzw. Macros
> ersetzt werden.

Falls atomic unterstützt wird.

Und Performance ist ja auch nicht ganz unwichtig.  Wenn atomic 
Bibliotheksfunktioe verwenden, die erst Locks allokieren um sie dann zu 
setzen, will man eher bei volatile bleiben.

von Peter D. (peda)


Lesenswert?

Johann L. schrieb:
> Wenn atomic
> Bibliotheksfunktioe verwenden, die erst Locks allokieren um sie dann zu
> setzen, will man eher bei volatile bleiben.

Und legt sich damit eine Zeitbombe. Wenn man auf einem ARM eine 64Bit 
Variable als volatile definiert, kann der Fehler jahrelang unentdeckt 
bleiben. Und plötzlich steuert die Mars-Rakete ins Nirvana.
Wenn der Zugriff atomar sein muß, dann ist volatile unzureichend.

Man kann sich aber im Mainkontext eine Tempvariable anlegen, die bei 
Funktionseintritt atomar von der Interruptvariable gelesen wird und an 
Ende atomar rückgeschrieben wird. Dann ist der Lockingoverhead 
vernachlässigbar.

von Wf88 (wf88)


Lesenswert?

Peter D. schrieb:
> Man kann sich aber im Mainkontext eine Tempvariable anlegen, die bei
> Funktionseintritt atomar von der Interruptvariable gelesen wird und an
> Ende atomar rückgeschrieben wird. Dann ist der Lockingoverhead
> vernachlässigbar.

Für mich persönlich ist es in dem Fall deutlich übersichtlicher, wenn 
ich da dann selber mit sei() und cli() hantiere. Ausserdem sehe ich so 
direkt den Overhead und muss nicht im Listing oder im Library-Code 
kontrollieren, was genau da an Overhead kommt.

Der atomic-Weg hat seinen Reiz, aber überzeugt bin ich davon noch nicht. 
Ich sollte vielleicht mal ein kleines Projekt auf atomic umbauen um ein 
Urteil zu fällen.

von Rolf M. (rmagnus)


Lesenswert?

(prx) A. K. schrieb:
> Das ist eine Gruppe von Wörtern, die man in jeder Reihenfolge
> hinschreiben kann: storage-class-specifier, type-specifier und
> type-qualifier.

Und function-specifier.

> Man kann sich also wie du Eselsbrücken bauen, muss aber im Kopf
> behalten, dass es innerhalb dieser Gruppe keine echten Regeln gibt.

Die Eselsbrücke dient dafür, um zu wissen, auf was ein type-qualifier 
wirkt, wenn Zeiger-Typen involviert sind. Das sind sie in deinem 
Beispiel aber nicht.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Peter D. schrieb:
> Wenn der Zugriff atomar sein muß, dann ist volatile unzureichend.

Ja, nur ist und war das schon immer so. Wer also bei einer Marsrakete 
oder auch sonst wo einen 64-Bit Variable volatile statt atomic 
definiert, macht halt einen Fehler. Das macht volatile nicht veraltet, 
man muß es wie atomic oder alles andere überhaupt nur richtig anwenden.

Im o.a. Fall eines volatile uint8_t brauchts kein atomic.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Wenn der Zugriff atomar sein muß, dann ist volatile unzureichend.

Ich hab ja auch nix anderes behauptet.

Aber __atomic_load zu verwenden wenn nix dahinter ist, ist bestimmt 
keine so gute Idee.

Und sei/cli oder ATOMIC_BLOCK sind ja kein Hexenwerk, und wenn man 
wirklich AVR → XYZ portiert, fällt das ja auf.

Bare Metal mit sher begrenzten Resources UND Compiler-Unterstützung ist 
eben nicht ganz die Liga von Boliden...

Und dass Interrupt-Sperren auf Multi-Core Systemen nix bringen (man kann 
ja schwerlich erst mal alle anderen Cores anhalten etc.) ist auch klar.

...und wenn man sowas wie __atomic_load z.B. im avr-gcc unterstützt, 
würde man das auch in IRQ-Sperre klammern; um die Latenz käme ma also 
auch dann nicht drumrum.

: Bearbeitet durch User
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.