Weil sie sich nicht in Hardware ändern.
Dieses Beispiel wird über Interrupts getriggert. Somit hat die Software
die volle Kontrolle über die Buffer. Bei einem DMA-Beispiel sieht das
sicher anders aus.
Oder verbreite ich hier gefährliches Halbwissen?
Little B. schrieb:> Oder verbreite ich hier gefährliches Halbwissen?
Ja. Was in einer Interruptroutine geändert wird, läuft außerhalb der
Kontrolle des Compilers und muss daher als volatile deklariert
werden, wenn darauf auch von außerhalb der Interruptroutine zugegriffen
werden soll.
Rufus Τ. F. schrieb:> Little B. schrieb:>> Oder verbreite ich hier gefährliches Halbwissen?>> Ja. Was in einer Interruptroutine geändert wird, läuft außerhalb der> Kontrolle des Compilers und muss daher als volatile deklariert> werden, wenn darauf auch von außerhalb der Interruptroutine zugegriffen> werden soll.
Das wäre grundsätzlich auch mein Verständnis. Die Frage ist höchstens,
ob es eine Rolle spielt, dass der Inhalt des buffers und der Input Index
nur von der Interrupt Routine selbst geändert wird und beim lesen nur
der output index geändert wird.
Wenn schon, muss man nur die Zeiger, die Anfang und Ende des Ringpuffers
markieren als volatile deklarieren.
Bei 8bit Controller verwende ich jedoch kleine Arrays mit maximal 255
Bytes als Puffer und dann keine Zeiger auf Anfang und Ende, sondern
indexe. Die beiden Indexe sind dann kleine 8bit Variablen. Und weil sie
8bit sind, entfällt die Synchronisation beim Zugriff.
Beim Interrupt wird nur der Ende-Index verändert.
Beim lesen aus dem Empfangspuffer wird nur der Anfang-Index verändert.
So brauche ich nichtmal diese beiden Zugriffe zu synchronisieren. Sie
dürfen ruhig "gleichzeitig" stattfinden.
Little B. schrieb:> Oder verbreite ich hier gefährliches Halbwissen?
Ja.
Also, wenn nur eine einzige Instanz auf eine Variable zugreift und auch
keinerlei Hardware hineinspukt, dann ist volatile überflüssig. Bei
Ringpuffern zum Beispiel gibt es genau 3 Dinge: den eigentlichen Puffer,
den Schreibzeiger und den Lesezeiger. Den Puffer braucht man nie als
volatile zu kennzeichnen. Bei den Schreib- und Lesezeigern hingegen ist
das etwas kritischer. Wenn man sein Inteface so schreibt, daß es
nichtblockierend ist, dann braucht man auch kein volatile. Wenn hingegen
jemand blockierend schreibt, dann sehr wohl, weil er ja mit seinem
Trampeln auf der Stelle seinen Dunstkreis nicht verläßt. Ich geb dir mal
ein Beispiel:
Hier siehst du, daß es bei nicht blockierender Schreibweise völlig egal
ist, ob da was volatile ist oder nicht, denn V24RxAvail1 wird immer
wieder aufgerufen und wartet niemals, und auch V24GetChar1 tut das
nicht. Da beide Funktionen immer wieder beendet werden und nie warten,
kann eine unbemerkte Änderung in U1Buf.InWP auch nicht passieren.
Klaro?
btw: der Beispielcode ist fragwürdig. Ich hätte da ein noch viel
schlimmeres Wort für solch kruden Mist. Sowas schreiben nur Leute, die
selber nie und nimmer jemals eine tatsächliche Anwendung geschrieben
haben, die auf sowas aufbaut.
W.S.
Ich hab's nicht getestet, aber es seiht gut aus.
Nur diese ternären Ausdrücke (p->rbTail = (p->rbTail > RXBUFFERSIZE-1) ?
0 : p->rbTail;) mag ich nicht. Ich finde, sie sind schwer lesbar. Aber
fachlich natürlich in Ordnung.
readUSART2Buffer() liefert eine 0 zurück, wenn der Puffer leer ist aber
auch wenn im Puffer eine 0 liegt. Dir fehlt eine Möglichkeit, diese
beiden Fälle zu unterscheiden.
Vorschläge:
1) Ändere den Rückgabetyp von readUSART2Buffer() auf int. Dann kannst du
im Fehlerfall -1 zurück geben.
2) Füge eine Funktion hinzu, mit der du abfragen kannst, ob der Puffer
leer ist.
3) Wenn du schon dabei bist, kann es auch bei einigen Anwendungen sehr
praktisch sein, zu wissen, ob im Puffer ein Zeilenumbruch liegt.
Und dann befasse dich mal mit der Konsole (stdin und stdout). Ich nehme
an, dass die C Library für STM32 das ebenso kann, wie die für AVR. Wenn
du diese beiden Kanäle mit dem seriellen Port verbindest, kannst du
sämtliche Funktionen der stdio.h und printf() benutzen.
Dieser Buffer wird von einem Bluetooth modul mit commands gefüttert,
eine 0 sollte deshalb eigentlich nicht im Buffer stehen - sonst ist dein
Einwand natürlich absolut gerechtfertigt.
W.S. schrieb:> Sowas schreiben nur Leute, die> selber nie und nimmer jemals eine tatsächliche Anwendung geschrieben> haben, die auf sowas aufbaut.
mit solchen Sätze ist das µC.net in Verruf gekommen.
W.S: Warum greifst du Leute, die vielleicht nicht so viel wissen wie du
so gemein an? Würdest du das mit deinen Freunden oder Frau und deinen
Kindern auch so machen?
Wie würdest du auf so einen Satz antworten?
ist wirklich nichts :)
Solche Dinge löst man mit einer "linked List" . Die Liste zeigt immer
auf die nächste Struktur , wie ein Domino rennt das ganze durch bis das
Ende erreicht ist. Das Ende ist markiert durch einen Null Pointer. Wenn
das Ende zum Anfang zeigt dreht sich das ganze im Kreis.
Hier die nötige Struktur die auf sich selber zeigt.
1
typedefstructnode{
2
void*data;
3
structnode*next;
4
}Node_t;
Nun die eigentliche Liste
1
typedefstructlinkedList{
2
3
Node_t*head;
4
Node_t*tail;
5
Node_t*current;
6
7
}LinkedList_t;
Nun müssen wir sie Initialisieren
1
voidinitList(LinkedList_t*list){
2
3
list->head=NULL;
4
list->tail=NULL;
5
list->current=NULL;
6
7
}
Nun benötigt man weitere Funktionen wie z.Bsp addHead(LinkedList_t
*list, void *data); addTail(LinkedList_t *list, void *data);
delete(LinkedList_t *list, void *data);
in diesen Funktionen werden die Pointer nur umcopiert.
Das Auslesen geht ungefähr so
1
Node_t*current=list->head;
2
3
while(current!=NULL){
4
5
readData(current->Data);
6
current=current->next;
7
8
}
Einmal angestoßen rennt das ganze solange bis NULL also "tail" erreicht
ist. Oder es hört die auf wenn es kein Ende gibt ;)
Im Netz gibt es Beispiele dieser Art...
Wer jetzt meint das wäre ja Verschwendung, der Data Pointer kann auch
auf ein Array etc. zeigen. Wenn die Länge des Telegramms bekannt ist
könnte jedes Commando in solch eine Struktur. Durch die Liste werden sie
dann abgearbeitet.
King J. schrieb:> /*> * Returns the char stored at the tail of the buffer> */> char readUSART2Buffer(void){> struct DataBuffer *p;> if(p->rbTail == p->rbHead){> return 0;> } else {> p->rbTail = (p->rbTail > RXBUFFERSIZE-1) ? 0 : p->rbTail;> return p->data[p->rbTail++];> }> }
Der Zeiger ist nicht initialisiert. Wo liegt das DataBuffer struct
überhaupt, den Teil hast Du vergessen. Und außerdem: Ich sehe kein
einziges volatile. Das ganze struct MUSS volatile sein, denn sowohl die
Indices als auch der Buffer selbst werden sowohl vom Interrupt als auch
von main() verwendet.
In diesem Zusammenhang:
Ich habe beobachtet daß beim gcc auch das Einziehen einer
Speicherbarriere vor dem Lesen einer nicht volatile Variablen beim gcc
scheinbar ein Lesen der Variable erzwingt (welches ohne die Barriere
herausoptimiert worden wäre).
1
asmvolatile("":::"memory");
ABER ist das tatsächlich ein garantierter Weg das zu erreichen oder
ist es nur Zufall daß der gcc sich so verhält? Oder anders gefragt:
Fällt eine (jede!) nicht volatile globale variable tatsächlich offiziell
unter die Kategorie "memory"? Steht das irgendwo geschrieben? Wenn dem
nämlich so wäre könnte man damit einiges vereinfachen, einen ganzen
Stall von volatile einsparen (so ziemlich alle) und temporären lokalen
Variablen um alle unnötigen volatile Zugriffe zu verhindern bräuchte man
auch nicht mehr. Dann würde es reichen an beiden Enden jeweils eine
Barriere zu errichten und fertig.
Jonas B. schrieb:> Weil man sicher stellen kann das die Zugriffe auf die Buffer atomar> sind.
volatile stellt nur sicher, dass alle Zugriffe auch tatsächlich
ausgeführt werden, nicht, dass dies atomar geschieht. volatile hat mit
Atomar...izität nichts zu tun.
Stefan U. schrieb:> Bei 8bit Controller verwende ich jedoch kleine Arrays mit maximal 255> Bytes als Puffer und dann keine Zeiger auf Anfang und Ende, sondern> indexe. Die beiden Indexe sind dann kleine 8bit Variablen. Und weil sie> 8bit sind, entfällt die Synchronisation beim Zugriff.
Gleiches Thema wie oben: volatile ist kein Synchronisationsmechanismus.
> Beim Interrupt wird nur der Ende-Index verändert.> Beim lesen aus dem Empfangspuffer wird nur der Anfang-Index verändert.> So brauche ich nichtmal diese beiden Zugriffe zu synchronisieren. Sie> dürfen ruhig "gleichzeitig" stattfinden.
Wie stellst du denn sicher, dass dein Ringpuffer nicht "unterläuft",
bzw. erkennst, ob er überläuft? Ich würde erwarten, dass irgendwo auch
ein Vergleich beider Indizes stattfindet.
W.S. schrieb:> Also, wenn nur eine einzige Instanz auf eine Variable zugreift und auch> keinerlei Hardware hineinspukt, dann ist volatile überflüssig. Bei> Ringpuffern zum Beispiel gibt es genau 3 Dinge: den eigentlichen Puffer,> den Schreibzeiger und den Lesezeiger. Den Puffer braucht man nie als> volatile zu kennzeichnen.
Warum nicht? Deine eigene vorherige Definition widerspricht dem. Es wird
von der ISR aus und vom Hauptprogramm aus drauf zugegriffen, also muss
es volatile sein. Es geht soweit ich weiß i.d.R. trotzdem, da Compiler
solche Arrayzugriffe normalerweise nicht wegoptimieren, aber im Prinzip
ist volatile dort ebenfalls erforderlich.
> Wenn man sein Inteface so schreibt, daß es nichtblockierend ist, dann> braucht man auch kein volatile. Wenn hingegen jemand blockierend> schreibt, dann sehr wohl, weil er ja mit seinem Trampeln auf der Stelle> seinen Dunstkreis nicht verläßt.
Das ist aber Frickelei, weil du da Annahmen darüber machst, wie der
Compiler optimiert.
> Hier siehst du, daß es bei nicht blockierender Schreibweise völlig egal> ist, ob da was volatile ist oder nicht, denn V24RxAvail1 wird immer> wieder aufgerufen und wartet niemals, und auch V24GetChar1 tut das> nicht.
Und? Warum sollte die Notwendigkeit für volatile davon abhängen, ob man
wartet oder nicht? Wenn man einen Wert lesen will, der außerhalb des
normalen Programmflusses verändert werden kann, braucht man volatile.
> Da beide Funktionen immer wieder beendet werden und nie warten,> kann eine unbemerkte Änderung in U1Buf.InWP auch nicht passieren.
Warum sollte es nicht? Es änddert eigentlich nicht wirklich was.
Faustregeln:
(1)
Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile
sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die
brauchen das alle.
(2)
Alles, was atomar sein muss, aber nicht zwingend durch die Hardware
atomar ist, muss in eine critical section. Das betrifft auf 8
Bit-Architekturen Read- und Write-Pointer.
(3)
1
asmvolatile("":::"memory");
sagt dem Compiler, dass der Speicher gerade unvorhersehbar geändert
wurde und alle in Registern gecachten Werte aus dem Speicher jetzt
ungültig sind. Das sollte also für so einen Anwendungsfall auf AVR
funktionieren. Auf anderen Architekturen kann es sein, dass du noch eine
Memory Barrier brauchst.
S. R. schrieb:> (1)> Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile> sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die> brauchen das alle.
Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur
gelesen wird sollte kein volatile benötigt werden.
> Hier siehst du, daß es bei nicht blockierender Schreibweise völlig egal> ist, ob da was volatile ist oder nicht, denn V24RxAvail1 wird immer> wieder aufgerufen und wartet niemals, und auch V24GetChar1 tut das> nicht. Da beide Funktionen immer wieder beendet werden und nie warten,> kann eine unbemerkte Änderung in U1Buf.InWP auch nicht passieren.
Das stimmt, wenn V24RxAvail1 als Funktion aufgerufen wird. Aber nicht
mehr, wenn V24RxAvail1 geinlined wird:
1
while(!V24RxAvail1());
Auf der anderen Seite bringt das Weglassen von volatile keinerlei
Vorteile, solange die Variable nur einmal innerhalb des Kontext
verwendet wird.
Viele Grüße, Stefan
Marco schrieb:> S. R. schrieb:>> (1)>> Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile>> sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die>> brauchen das alle.>> Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur> gelesen wird sollte kein volatile benötigt werden.
Dem füge ich noch hinzu ich hatte erst kürzlich ein Problem wo ich 2
Volatile variablen hintereinander beschrieben hatte - dies hatte zu
einem hardfault geführt. Nachdem ich einen rausgenommen hatte hat es
problemlos funktioniert.
Also mit volatile sollte man grundsätzlich schon so sparsam wie möglich
umgehen, einfach volatile vorsichtshalber stehen lassen ist keine gute
Idee.
Marco schrieb:> S. R. schrieb:>> (1)>> Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile>> sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die>> brauchen das alle.>> Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur> gelesen wird sollte kein volatile benötigt werden.
Nein, wer liest und wer schreibt, spielt keine Rolle.
Stefan K. schrieb:> Auf der anderen Seite bringt das Weglassen von volatile keinerlei> Vorteile, solange die Variable nur einmal innerhalb des Kontext> verwendet wird.
Und wenn sie mehrmals verwendet wird, ist es besser, den Wert in einer
lokalen Variable zwischenzuspeichern und die stattdessen zu nehmen, so
dass die Austausch-Variable wieder nur einmal verwendet wird. Damit ist
auch ein atomarer Zugriff leichter, weil man nicht kreuz und quer durch
den ganzen Code darauf zugreift und dann aufpassen muss, nicht in einen
inkonsitenten Zustand zu kommen, weil die eine Hälfte noch mit einem
alten Wert rechnet und die andere schon mit einem inzwischen
eingetroffenen neuen Wert.
Das ist natürlich schwieriger, wenn man, wie von W.S. vorgeschlagen, die
Zugriffe auf separate Funktionen verteilt, geht aber zur Not per
Parameter-Übergabe auch.
Marco schrieb:> Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur> gelesen wird sollte kein volatile benötigt werden.
Dann kann es passieren, dass die isr einen Wert benutzt, der im
Hauptprogramm längst verändert, aber noch nicht wieder in die Variable
zurück geschrieben wurde.
Bei einem Input-Buffer kann dann die isr einen Buffer-Overflow
feststellen,
obwohl das Hauptprogramm schon längst Zeichen abgeholt hat.
Bei einem Output-Buffer kann es passieren, dass die Output-isr enabled
wird und dann als erstes feststellt, dass sie nichts zu senden hat, da
das Hauptprogramm seinen Index noch nicht zurück in die Variable
geschrieben hat.
Ich gebe zu, dass diese Szenarien etwas konstruiert werden müssen. Auf
der anderen Seite gibt es in diesem Zusammenhang kaum Szenarien, wo der
Compiler ohne volatile besser optimieren kann.
Gruß, Stefan
A variable should be declared volatile whenever its value could change
unexpectedly. In practice, only three types of variables could change:
1. Memory-mapped peripheral registers
2. Global variables modified by an interrupt service routine
3. Global variables accessed by multiple tasks within a multi-threaded
application
We'll talk about each of these cases in the sections that follow.
http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
---
Sprich bei einem normalen Programm mit Interrupt - und wenn im Interrupt
nur gelesen wird - dann wird meiner Ansicht nach kein volatile benötigt.
Marco schrieb:> Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur> gelesen wird sollte kein volatile benötigt werden.
Aus Sicht des Hauptprogramms ist die ISR ein externer Zugriff, aus Sicht
der ISR ist das Hauptprogramm ein externer Zugriff. Das ist ein
symmetrisches Problem.
Das wird speziell dann interessant, wenn Hauptprogramm und ISR
gleichzeitig laufen können (Multicore-CPUs, DMA o.ä.)
DerFreud schrieb:> W.S: Warum greifst du Leute, die vielleicht nicht so viel wissen wie du> so gemein an? Würdest du das mit deinen Freunden oder Frau und deinen> Kindern auch so machen?> Wie würdest du auf so einen Satz antworten?
Wenn ich jemanden angreifen würde, dann sähe das ganz anders aus. Ich
kann sowas, kannste glauben.
Du bist mit deinen Ausführungen deutlich zu zimperlich. Wir schreiben
hier nicht über Händchenhalten oder Eia-Popeia, sondern über Technik.
W.S.
Rolf Magnus schrieb:> Warum nicht? Deine eigene vorherige Definition widerspricht dem. Es wird> von der ISR aus und vom Hauptprogramm aus drauf zugegriffen, also muss> es volatile sein.
Du irrst.
Also noch einmal das Ganze:
1. es gibt einen Puffer, wo die Bytes (oder sonstwas) hineinkommen.
Dieser Puffer braucht niemals als volatile gekennzeichnet zu werden, wie
ich bereits schrieb.
2. es gibt zwei Indizes, die in den Puffer hineinzeigen. Einer der
Indizes wird von der ISR verwaltet und der andere von der Leseroutine.
Den jeweils eigenen Index verändert die Instanz, den anderen liest sie
nur einmalig und verändert ihn nicht.
Verstehe mal das Prinzip: Da schreibt man einen HW-Treiber, der sowohl
die ISR, als auch die Initialisierung als auch die im Headerfile
ausgewiesenen Schnittstellen-Funktionen enthält. Alle diese Funktionen
werden von ganz woanders aufgerufen und warten nicht auf irgendwelche
Änderungen der von ihnen nicht verwalteten Indizes. Sie können sich
auch in ihrem Kontext niemals etwas unter der Hand merken (was ja genau
das ist, was man per volatile zu unterbinden trachtet).
Logischerweise gibt es nur einen einmaligen Gebrauch, zu diesem Gebrauch
muß dieser Index einmalig geladen werden und das war's.
Ebenso logischerweise ist ein Inline für Funktionen, die im Headerfile
als extern deklariert sind, nicht möglich.
Ist das jetzt etwas klarer?
W.S.
S. R. schrieb:> Faustregeln:>> (1)> Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile> sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die> brauchen das alle.
Auch du, Brutus?
Nein, so etwas zu sagen, ist schlichtweg falsch. Allerdings stört es
niemanden, wenn man vor all so etwas 'volatile' davorschreibt.
Der Knackpunkt ist ja, daß volatile einzig nur dazu benutzt wird, um den
Compiler davon abzuhalten, einen echten Zugriff auf das 'ding'
wegzuoptimieren. Sowas trifft man stets, wenn irgendwo blockierend
geschrieben wurde:
while (wichtigesFlag) warte();
Wenn man hingegen im gerade lokalen Kontext NICHT auf irgendwas wartet,
sondern das wichtige Flag oder so nur ein einziges mal abfragt, dann
kann ein Wegoptimieren nicht stattfinden. Folglich ist es schnurz, ob
dort volatile steht oder nicht.
W.S.
W.S. schrieb:> S. R. schrieb:>> Faustregeln:>>>> (1)>> Alles, was in der ISR und im Hauptprogramm benutzt wird, muss volatile>> sein. Das betrifft den Puffer, den Read-Index und den Write-Index, die>> brauchen das alle.>> Auch du, Brutus?>> Nein, so etwas zu sagen, ist schlichtweg falsch. Allerdings stört es> niemanden, wenn man vor all so etwas 'volatile' davorschreibt.
Darum schrieb ich "Faustregeln" oben drüber. Hältst du dich dran,
bekommst du keine falschen Ergebnisse, hältst du dich nicht dran,
solltest du es für jeden Einzelfall verdammt gut begründen können.
> Der Knackpunkt ist ja, daß volatile einzig nur dazu benutzt wird, um den> Compiler davon abzuhalten, einen echten Zugriff auf das 'ding'> wegzuoptimieren.
Richtig.
> Sowas trifft man stets, wenn irgendwo blockierend> geschrieben wurde:
Aber nicht ausschließlich!
> Wenn man hingegen im gerade lokalen Kontext NICHT auf irgendwas wartet,> sondern das wichtige Flag oder so nur ein einziges mal abfragt, dann> kann ein Wegoptimieren nicht stattfinden.
Heutige Compiler können das nicht wegoptimieren, richtig. Aber kein
Compiler garantiert dir das.
Wenn du den Optimizer deines Compilers kennst und dich auf dessen
Implementation verlässt, oder du durch die Struktur deines Codes den
Optimizer hinreichend behinderst, kannst du dir das volatile auch
sparen.
Dafür riskierst du, dass dein Code irgendwann mal kaputtgeht. Sei es
durch bessere Compiler (in den letzten 20 Jahren ist da viel passiert -
in den nächsten 20 Jahren wird da viel passieren) oder durch eine
veränderte Umgebung (Multicore).
S. R. schrieb:> Marco schrieb:>> Du meinst alles was in der ISR beschrieben wird. Wenn in der ISR nur>> gelesen wird sollte kein volatile benötigt werden.>> Aus Sicht des Hauptprogramms ist die ISR ein externer Zugriff, aus Sicht> der ISR ist das Hauptprogramm ein externer Zugriff. Das ist ein> symmetrisches Problem.
Selbst aus Sicht des Hauptprogramms alleine egibt sich das Problem
schon. Ist ja nicht anders, als bei I/O-Registern. Denn der Compiler
könnte ja sowohl Lese-, als auch Schreibzugriffe wegoptimieren.
W.S. schrieb:> Also noch einmal das Ganze:> 1. es gibt einen Puffer, wo die Bytes (oder sonstwas) hineinkommen.> Dieser Puffer braucht niemals als volatile gekennzeichnet zu werden, wie> ich bereits schrieb.
Das hast du bereits geschrieben. Und ich habe geschrieben, dass das
nicht stimmt.
> Verstehe mal das Prinzip: Da schreibt man einen HW-Treiber, der sowohl> die ISR, als auch die Initialisierung als auch die im Headerfile> ausgewiesenen Schnittstellen-Funktionen enthält. Alle diese Funktionen> werden von ganz woanders aufgerufen und warten nicht auf irgendwelche> Änderungen der von ihnen nicht verwalteten Indizes. Sie können sich> auch in ihrem Kontext niemals etwas unter der Hand merken (was ja genau> das ist, was man per volatile zu unterbinden trachtet).
Das heißt, du trennst es in eigene Funktionen, die du alle in separate
C-Files schreibst, nur damit der Compiler kein Inlinig betreiben kann
und du dir so das volatile sparen kannst? Und dann kommt gcc -flto und
betreibt Inlining über die Grenzen der einzelnen C-Files hinweg.
Wie ich schon sagte: Gefrickel, weil Annahmen über das
Optimierungsverhalten des Compilers getroffen werden. So entsteht dann
der Code, der mit neueren Compiler nicht mehr tut. Es gibt da immer drei
Möglichkeiten: Man schreibt korrekten Code, man schreibt unbewusst
falschen code - oder man schreibt bewußt falschen Code, weil er ja
trotzdem funktioniert (zumindest im Moment).
Die Regel ist doch ganz einfach: Variablen, bei denen es essenziell ist,
dass alle Zugriffe darauf auch statt finden, brauchen volatile. Und das
schreibt man natürlich auch dann hin, wenn man einen Spezialfall
gefunden hat, in dem mit dem aktuellen Compiler der Zugriff auch ohne
volatile stattfindet.
> Ebenso logischerweise ist ein Inline für Funktionen, die im Headerfile> als extern deklariert sind, nicht möglich.
Das ist falsch. Siehe -flto.