Wo ist der Unterschied: volatile uint8_t oder uint8_t volatile ?

Beitrag #7575984 wurde von einem Moderator gelöscht.
#7576392
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

#7581737
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.

#7582359
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.

#7582484
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.

#7582600
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.

#7582678
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.

#7582683
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.

#7582685
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.

#7582687
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

Persönliche Seite #7582706
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.

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