Forum: Mikrocontroller und Digitale Elektronik uC: Adresse von Empfangspuffer an andere Funktion übergeben


von Anton (Gast)


Lesenswert?

Guten Morgen Leute!

Ich habe mal eine Frage bezüglich Pointer in C. Ein uC empfängt über nen 
FT232 Kommandos vom PC. Klappt alles bestens. Ich habe eine uart.c und 
uart.h, welche im Hintergrund interruptgesteuert Zeichen empfängt und 
bei einer fertigen Nachricht ein Flag setzt, sodass das Hauptprogramm 
die Nachricht auswerten kann.

In der uart.c habe ich einen Empfangspuffer vom Typ
1
char uart_rx_buffer[UART_RX_BUFFER_SIZE]

Jetzt soll mein Hauptprogramm auf den Inhalt im Puffer zugreifen können. 
Also habe ich ebenfalls in der uart.c eine Funktion
1
const char * uart_return_rx_buffer_address( void )
2
{
3
  return (const char *) uart_rx_buffer;
4
}

Frage: Ist es so generell erstmal der richtige Weg, oder "macht" man das 
anders / gibt es einen besseren Weg?

Frage 2: Dass meine Funktion const char * zurückliefert, ist doch wohl 
richtig so, da der Empfänger der Pufferadresse den Inhalt ja nicht 
verändern, sondern nur lesen soll.

Aber: Wenn ich den cast mit (const char *) nicht mache, dann meckert der 
Compiler - kein Fehler, aber eine Warnung. Der uart_rx_buffer ist von 
sich aus natürlich nicht const, der wird ja verändert. Aber ohne den 
cast passt der uart_rx_buffer nicht zum Typ des Rückgabewertes der 
Funktion.

Ist der cast da also unbedingt notwendig, oder sollte man einfach die 
Warnung ignorieren?

Gruß und danke!

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Anton schrieb:
>
1
> const char * uart_return_rx_buffer_address( void )
2
> {
3
>   return (const char *) uart_rx_buffer;
4
> }
5
>

Hallo!

So wie es aussieht, hast du deine Funktion so deklariert, dass sie 
"const char*" zurückgibt. Der Compiler passt deswegen darauf auf, dass 
der per return-Anweisung zurückgegebene Wert dem entspricht.

Alternative: Deklariere den Rückgabewert deiner Funktion nicht als 
"const char*", sondern als "char*" (erste Zeile in o.g. Code).

von Peter II (Gast)


Lesenswert?

Anton schrieb:
> Ist der cast da also unbedingt notwendig, oder sollte man einfach die
> Warnung ignorieren?

sollte eigentlich ohne Warnung gehen. Welche Warnung bekommst du genau?

von Anton (Gast)


Lesenswert?

Markus Weber schrieb:
> Alternative: Deklariere den Rückgabewert deiner Funktion nicht als
> "const char*", sondern als "char*" (erste Zeile in o.g. Code).

Hi!

Klar, das würde schon gehen, nur dann fehlt mir ja die Restriktion, den 
Puffer von außen nicht verändern zu können.

von Anton (Gast)


Lesenswert?

Peter II schrieb:
> sollte eigentlich ohne Warnung gehen. Welche Warnung bekommst du genau?

#121-D return value type does not match the function type

von Blablubb (Gast)


Lesenswert?

Hallo,

das kannst Du so machen, mir persönlich würde eine Variante mit FIFO 
besser gefallen, weil Dein Ansatz ein Timingproblem aufwirft: Wenn das 
erste Byte des nächsten Befehls kommt und Deine Verarbeitung noch nicht 
fertig ist, musst Du Dich irgendwie verrenken, um das Byte zu "retten". 
Ein FIFO sorgt hier für ein deutlich entspannteres Timing und die Kiste 
wird robuster.

Ein char[] ist etwas anderes als ein const char*, deshalb meckert der 
Compiler. Allerdings ist es "legal" die Startadresse eines char[] in 
einen const char* Zeiger zu casten. Compilerwarnungen zu ignorieren ist 
nie eine gute Idee.

Um zu verhindern, dass Dein const char* von oben geschrieben wird, 
müsstest Du ihn als "const char const*" deklarieren (ohne Garantie, 
meistens halte ich es nicht so strikt :-).

Da Du auf Dein char[] aus dem main()- und dem ISR-Kontext zugreifst, 
wäre eine Deklaration als "volatile" zu empfehlen.

Grüße,
Gast

von Anton (Gast)


Lesenswert?

Blablubb schrieb:
> das kannst Du so machen, mir persönlich würde eine Variante mit FIFO
> besser gefallen, weil Dein Ansatz ein Timingproblem aufwirft: Wenn das
> erste Byte des nächsten Befehls kommt und Deine Verarbeitung noch nicht
> fertig ist, musst Du Dich irgendwie verrenken, um das Byte zu "retten".
> Ein FIFO sorgt hier für ein deutlich entspannteres Timing und die Kiste
> wird robuster.

Generell stimme ich zu! Das ist hier aber nicht nötig, da immer nur ein 
Befehl abgearbeitet wird und darauf eine Antwort erfolgt. Das ist 
gewollt und soll auch so bleiben. Erst wenn die Antwort gesendet wurde, 
kann ein neuer Befehl empfangen werden. Wärend der Befehl bearbeitet 
wird, ist das weitere Empfangen gesperrt.

Blablubb schrieb:
> Allerdings ist es "legal"

Legal ist gut...aber ist es so der richtige Weg?

Blablubb schrieb:
> Compilerwarnungen zu ignorieren ist
> nie eine gute Idee.

Deswegen frage ich nach :)

Blablubb schrieb:
> Um zu verhindern, dass Dein const char* von oben geschrieben wird,
> müsstest Du ihn als "const char const*" deklarieren

Noch ein const? Kannst du mir kurz erklären, was welches const dann 
bewirkt? Da kann ich dann nämlich nicht mehr folgen. Dachte mit einem 
const wäre es erledigt. Leider finde ich solche "komplizierten" Gebilde 
auch nicht wirklich in irgendeiner Literatur.

Blablubb schrieb:
> Da Du auf Dein char[] aus dem main()- und dem ISR-Kontext zugreifst,
> wäre eine Deklaration als "volatile" zu empfehlen.

Sorry, vergessen zu schreiben. Mein rx_buffer ist vom Typ volatile char. 
Macht das dann noch einen Unterschied bei der Übergabe der Adresse? Muss 
ich dann const volatile char?

...Oh man - das wird langsam etwas kompliziert.

von stefanus (Gast)


Lesenswert?

> Mein rx_buffer ist vom Typ volatile char. Macht das dann noch einen
> Unterschied bei der Übergabe der Adresse? Muss ich dann const
> volatile char?

Gute Frage, das wüsste ich auch gerne.

> ...Oh man - das wird langsam etwas kompliziert.

Deswegen wurden inzwischen andere Programmiersprachen erfunden, bei 
denen solche Fragen (zumindest in der Theorie) gar nicht erst aufkommen 
sollen.

Wobei ich davon ausgehe, das ich die Ausrottung von C nicht mehr erleben 
werde. Darum bleibe ich lieber Freund von C. So hat man weniger Feinde 
:-)

von Anton (Gast)


Lesenswert?

OK, jetzt bin ich in der Tat ein wenig verunsichert.

Also nochmal zusammengefasst:

In der uart.c
1
volatile char rx_buffer[RX_BUFFER_SIZE];
In diesen Puffer kommen per UART die Zeichen. Von woanders möchte ich 
auf diesen Puffer zugreifen, also möchte ich gerne die Adresse 
übergeben. Was ist jetzt die richtige Funktion dafür?

Zum Verständnis - ist es so erstmal richtig?
1
char * function( void ) // gibt einen veränderbaren Zeiger auf einen veränderbaren char zurück
2
3
const char * function( void ) // gibt einen veränderbaren Zeiger auf einen nicht-veränderbaren char zurück
4
5
char * const function( void ) // gibt einen nicht-veränderbaren Zeiger auf einen veränderbaren char zurück
6
7
const char * const function( void ) // gibt einen nicht-veränderbaren Zeiger auf einen nicht-veränderbaren char zurück

- Was ist jetzt mit dem volatile von rx_buffer? Muss das hier auch noch 
mit rein?

- In welchem Fall brauche ich einen nicht-veränderbaren Zeiger? Bzw. in 
wie fern könnte ich diesen verändern? Hat da mal einer ein Beispiel?

- Welche Variante wäre jetzt für mich die richtige?


Sorry, aber das ist für mich echt kompliziert.

von Anton (Gast)


Lesenswert?

Nachtrag:
1
const volatile char * const uart_return_buffer_address( void )

bewirkt vom Compiler:

#869-D type qualifier on return type is meaningless


Kann mich mal bitte jemand aufklären?

von foo (Gast)


Lesenswert?

Anton schrieb:
> #869-D type qualifier on return type is meaningless
>
> Kann mich mal bitte jemand aufklären?

Das vor dem Stern (*) gibt an worauf gezeigt wird, und da ist const 
volatile nun mal sinnlos.
Nach dem Stern kann man ein const hinzufügen, um dem Compiler zu sagen, 
der Pointer soll nicht verändert werden.

Also entweder:
1
volatile char *
oder
1
volatile char * const

von foo (Gast)


Lesenswert?


von Anton (Gast)


Lesenswert?

foo schrieb:
> Das vor dem Stern (*) gibt an worauf gezeigt wird

OK, gezeigt wird auf ein volatile char

foo schrieb:
> Nach dem Stern kann man ein const hinzufügen, um dem Compiler zu sagen,
> der Pointer soll nicht verändert werden.

Damit kann dann der Inhalt des Zeigers, also die Adresse des rx_buffers 
nicht geändert werden.

Aber der Inhalt des rx_buffers ist doch mit
1
volatile char * const
nach wie vor veränderbar, oder? Bitte versteht mich nicht falsch, man 
könnte jetzt auch sagen, dass ich ja wohl wissen muss, wo mein Programm 
wann was ändert, aber ich möchte diese Geschichte einfach mal verstehen.

von Peter II (Gast)


Lesenswert?

das volatile ist eigentlich überhaupt nicht notwendig. In der ISR wo die 
Daten reingeschrieben werden kann er eh nichts im Register vorhalten.

und bei zugriff über eine Zeiger im normalen Programm erfolgt auch nicht 
über Register. (außer man nimmer immer das x. element vom Array, was 
aber in der Praxis kaum vorkommt.)

Ich würde hier da volatile komplett weglasse, es stört mehr als es 
bringt.

von foo (Gast)


Lesenswert?

Anton schrieb:
> Aber der Inhalt des rx_buffers ist doch mitvolatile char * const
> nach wie vor veränderbar, oder? Bitte versteht mich nicht falsch, man
> könnte jetzt auch sagen, dass ich ja wohl wissen muss, wo mein Programm
> wann was ändert, aber ich möchte diese Geschichte einfach mal verstehen.

Dann musst halt casten, so wie in deinem Code.
1
return (const char *) uart_rx_buffer;

Der Compiler warnt dich nur, dass du das volatile verlierst.
Volatile heißt ja, da Zugriffe, also auch Lesen Seiteneffekte haben 
können. Bei deinem Buffer egal, bei Registern manchmal nicht.

von Anton (Gast)


Lesenswert?

foo schrieb:
> dass du das volatile verlierst

Macht das denn nichts aus? Es heißt doch, dass alle Variablen, welche 
durch einen Interrupt verändert werden können, als volatile zu 
deklarieren sind.

Das ist hier der Fall, da die UART-ISR den Puffer beschreibt. Wenn ich 
jetzt die Adresse des Puffers an eine andere Funktion weitergebe, 
brauche ich das volatile dann nicht mehr mit zu schleppen? Ich verstehe 
den Zusammenhang dann nicht.

Volatile garantiert mir doch, dass stets der Inhalt neu aus dem Speicher 
geholt wird und ich somit immer den aktuellen Wert geliefert bekomme. 
Wenn ich nun die Adresse weitergebe will ich ja auch den aktuellen Wert.

Oder ist dies hier nicht nötig, da ich mit der Adresse ja sowieso genau 
in den Speicher zeige, wo sich der gesuchte Inhalt tatsächlich befindet? 
Aber warum muss dann ein Array ebenfalls als volatile deklariert werden? 
Muss nicht eh in die Speicherstelle geschrieben werden? Oder ist das 
ganze Array noch irgendwo quasi "kopiert" und wird sonst nur "dort" 
verändert?

von Karl H. (kbuchegg)


Lesenswert?

Anton schrieb:
> foo schrieb:
>> dass du das volatile verlierst
>
> Macht das denn nichts aus?

Bei dir macht das nichts aus.
Das ist aber in deinem speziellen Programmaufbau so. Im Allgemeinen kann 
aber der Compiler nicht wissen, ob das harmlos ist oder nicht, daher 
warnt er dich, dass du damit (indirekt) das volatile umgehst bzw. 
verlierst.


Die Warnung vom Anfang bezog sich nicht auf das const, sondern darauf, 
dass du damit das volatile verlierst.

> Es heißt doch, dass alle Variablen, welche
> durch einen Interrupt verändert werden können, als volatile zu
> deklarieren sind.

Das ist die Kurzfassung.
Die etwas längere Fassung hat nichts mit Interrupts zu tun. DIe längere 
Fassunf besagt, das mit volatile dem Compiler verboten wird, 
irgendwelche Annahmen über den Inhalt der Variblen zu treffen, weil sie 
sich auf Wegen ver#ndern kann, die für den COmpiler nicht einsichtig 
sind.

Hast du also
1
  char c;
2
  char * pPtr =  uart_return_rx_buffer_address();
3
4
  c = *pPtr;
5
  c = *pPtr;   // das sind absichtlich 2 identische Zugriffe!
dann kann in diesem Fall der COmpiler den 2.ten Zugriff wegoptimieren, 
weil sich weder pPtr, noch *pPtr in diesem Codestück ändern können. c 
kriegt also in beiden Fällen aus Sicht des Compilers jeweils denselben 
Wert. Daher kann er den 2.ten Zugriff weglassen.
Wohingegen man dem Compiler hier
1
  char c;
2
  volatile char * pPtr =  uart_return_rx_buffer_address();
3
4
  c = *pPtr;
5
  c = *pPtr;   // das sind absichtlich 2 identische Zugriffe!
dem Compiler explizit mitteilt: das, worauf pPtr zeigt, das kann sich 
ändern, ohne das du das mitkriegst. Du musst also beide Zugriffe *pPtr 
tatsächlich durchführen!

> Das ist hier der Fall, da die UART-ISR den Puffer beschreibt.

Schau.
Der COmpiler analysiert nicht den Sinn deiner Programmzeilen. Der 
Compiler geht nach formalen Regeln vor. Und die besagen nun mal, das der 
Verlust eines volatile potentiell ein Problem sein kann. Und das teilt 
dir der Compiler mit der Warnung mit. Ob das dann in deinem konkreten 
Fall tatsächlich problematisch ist oder nicht, das musst du entscheiden. 
Du bist ja immerhin der Programmierer. Wer, wenn nicht er sollte wissen, 
ob das 'Problem' harmlos ist oder nicht?

> Volatile garantiert mir doch, dass stets der Inhalt neu aus dem Speicher
> geholt wird und ich somit immer den aktuellen Wert geliefert bekomme.
> Wenn ich nun die Adresse weitergebe will ich ja auch den aktuellen Wert.

Aber der Aufrufer kann diese Adresse mehrmals benutzen um mehrmals auf 
dieselbe Speicherzelle zuzugreifen!
1
  char * pPtr =  uart_return_rx_buffer_address();
2
3
  while( *pPtr != 'a' )
4
    ;

von Peter D. (peda)


Lesenswert?

Anton schrieb:
> Frage: Ist es so generell erstmal der richtige Weg, oder "macht" man das
> anders / gibt es einen besseren Weg?

Generell würde ich keine überflüssigen Parameter übergeben, sondern 
direkt auf den (einen) Empfangspuffer zugreifen.

Die Übergabe der Pufferadresse ist nur dann nötig, wenn man mehrere 
Puffer zur Auswahl hat.
Und falls die Funktion universell sein soll, muß man auch die 
Puffergröße als Parameter übergeben.

von Anton (Gast)


Lesenswert?

Hallo Karl-Heinz!

Vielen Dank für deine ausführliche Antwort.

In meinem Fall - wie ist es denn jetzt richtig? den RX-Puffer deklariere 
ich als volatile char.

und wenn ich jetzt die adresse dieses Puffers weitergeben möchte, dann 
erfolgt das mit dem volatile
1
volatile char * uart_return_rx_buffer_address( void )
2
{
3
  return uart_rx_buffer;
4
}
oder ohne:
1
char * uart_return_rx_buffer_address( void )
2
{
3
  return (char *) uart_rx_buffer;
4
}
Durch den Cast verschwindet dabei die Warnung vom Compiler.

Wenn ich jetzt zusätzlich den Schreibschutz auf den Puffer haben will, 
dann so:
1
const char * uart_return_rx_buffer_address( void )
2
{
3
  return (const char *) uart_rx_buffer;
4
}
oder so:
1
volatile const char * uart_return_rx_buffer_address( void )
2
{
3
  return (volatile const char *) uart_rx_buffer;
4
}
Oder mache ich den Zeiger ebenfalls const, wo ich mich aber frage, wofür 
ich das hier brauchen sollte.

Wenn ich jetzt irgendwo einen Zeiger in der main() ins Leben rufe, 
welcher die Adresse speichern soll, die mir von der Funktion geliefert 
wird, muss ich diesen dann immer identisch zum Rückgabewert der Funktion 
wählen? Also für das letzte Beispiel dann
1
volatile const char * zeiger = uart_return_rx_buffer_address();

Danke für die Hilfe.

von Anton (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Generell würde ich keine überflüssigen Parameter übergeben, sondern
> direkt auf den (einen) Empfangspuffer zugreifen.

Ich habe ja nur den einen. Aber der ist in der uart.c und daher kann ich 
so nicht darauf zugreifen, es sei denn, ich deklariere den als extern. 
Daher dachte ich, ich lasse mir die Adresse von dem geben.

von Anton (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Generell würde ich keine überflüssigen Parameter übergeben

Was meinst du mit "überflüssigen"?

von Karl H. (kbuchegg)


Lesenswert?

Anton schrieb:
> Hallo Karl-Heinz!
>
> Vielen Dank für deine ausführliche Antwort.
>
> In meinem Fall - wie ist es denn jetzt richtig?

Indem du darüber nachdenkst, wie der Aufrufer deiner Funktion den 
Pointer verwenden wird!

> den RX-Puffer deklariere
> ich als volatile char.

Das kann man schon mal in Frage stellen, ob das in deinem Fall überhaupt 
notwendig ist.

Sorry. Aber man kann in der Programmierung nur ganz selten nach dem 
Muster "Ich mach das einfach so und so, nach Kochrezept" vorgehen. Man 
muss jeden Fall einzeln durchdenken. In deinem konkreten Fall musst du 
sowohl dein UART Modul, als auch wie es verwendet wird ins Kalkül 
ziehen.
Genauso beim volatile. volatile hat den Zweck ein ganz bestimmtes 
Problem zu verhindern. Es liegt an dir zu entscheiden, ob dieses Problem 
bei dir überhaupt auftreten kann oder nicht. Einfach nur zu sagen "Alle 
Variablen, die in einer ISR vorkommen mach ich volatile", das greift 
deutlich zu kurz.


> Durch den Cast verschwindet dabei die Warnung vom Compiler.

Logisch.
So ein Cast ist ja letzten Endes nichts anderes als ein
"Ja, ich weiß das die Datentypen rein formal nicht passen. Aber, hör 
mal, ich bin der Programmierer und ich übernehme die Verantwortung, dass 
das in diesem Fall ok ist. Also: Halts Maul!"

von Anton (Gast)


Lesenswert?

Ich muss zugeben, dass ich mich mit den Zeigern und jetzt scheinbar auch 
mit dem volatile etwas schwer tue - das sieht man sicher auch. Dass der 
RX-Puffer von mir ein volatile bekommen hat, geht unter anderem auch 
daher hervor, dass es hier in der UART-Erklärung auf mikrocontroller.net 
so gemacht wird.

Ich gebe zu, dass ich nach deiner Erklärung garnicht mehr so sicher bin, 
ob volatile ja oder nein. Brauche ich es hier oder nicht. Ich weiß es 
nicht!

Ebenso mit der Übergabe der Adresse und der weiteren Verarbeitung durch 
einen Zeiger, welcher den Rückgabewert aufnimmt. Ich weiß, dass es vllt. 
etwas viel verlangt ist, aber kann es mir evtl. mal jemand anhand meines 
Beispieles hier erklären wie es richtig ist UND vor allem WARUM?

Mein Code funktioniert im Prinzip schon problemlos, aber ich will ja 
nicht dumm sterben und es gerne richtig machen. Unnötig volatiles 
verteilen ist ja auch nicht Sinn der Sache und anderes umcasten, obwohl 
ggf. garnicht nötig, das will ich auch nicht.

Könnte mir jemand den gefallen tun - ich denke mit einer Erklärung 
anhand des vorliegenden Beispiels kann ich es dann hoffentlich auch auf 
andere Fragestellungen anwenden. Sonst stehe ich demnächst wieder hier.


Gruß, Anton

von Peter D. (peda)


Lesenswert?

Volatile ist in der Tat ein schwieriges Problem. Und wenn man das 
volatile noch als Parameter übergeben will, wird das immer komplexer.

Das volatile Problem entsteht dadurch, daß der Compiler Schleifen 
erkennt und dann möglichst viele Zugriffe vor die Schleife verlagern 
will.
Dazu kommt der weitere Aspekt, daß er Funktionen inlinen möchte und 
damit deren Zugriffe plötzlich zur Schleife gehören und mit optimiert 
werden können.

Interrupts werden aus Sicht des Compilers niemals aufgerufen und damit 
ist er der Meinung, die Variable ändert sich nicht innerhalb der 
Schleife, kann also davor einmalig in ein Register gelesen werden.

Eine Lösung ist, nur den kritischen Zugriff als volatile zu casten:
1
// force access of interrupt variables
2
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
3
4
static uint8_t rx_in;
5
static uint8_t rx_out;
6
7
uint8_t ukbhit0()
8
{
9
  return rx_out ^ IVAR(rx_in);    // rx_in modified by interrupt !
10
}

von Anton (Gast)


Lesenswert?

Peter Dannegger schrieb:
> #define IVAR(x)         (*(volatile typeof(x)*)&(x))

Oh man...solche Ausdrücke...
1
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
...geben mir zu verstehen, dass ich noch viel lernen muss :-\

von foo (Gast)


Lesenswert?

Anton schrieb:
> Oh man...solche Ausdrücke...#define IVAR(x)         (*(volatile
> typeof(x)*)&(x))
> ...geben mir zu verstehen, dass ich noch viel lernen muss :-\

Warum? Einfach auflösen.
1
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
aus:
1
IVAR(rx_in)
wird
1
(*(volatile unsigned char*)&(rx_in))
Jetzt klar?


Wobei man sagen muss, dass typeof nicht ANSI ist, sondern eine GNU 
extension.

Man könnte auch ANSI konform schreiben.
1
#define IVAR(x,y)         (*(volatile y*)&(x))
2
3
static uint8_t rx_in;
4
static uint8_t rx_out;
5
6
uint8_t ukbhit0()
7
{
8
  return rx_out ^ IVAR(rx_in, uint8_t);    // rx_in modified by interrupt !
9
}

von Juergen (Gast)


Lesenswert?

Anton schrieb:
>
1
> const volatile char * const uart_return_buffer_address( void )
2
>
>
> bewirkt vom Compiler:
>
> #869-D type qualifier on return type is meaningless

Das bezieht sich wohl auf das const hinter dem *.
Das ist sinnlos, weil es ein Rückgabe*wert* und keine Variable ist.

Jürgen

von Juergen (Gast)


Lesenswert?

Blablubb schrieb:
> müsstest Du ihn als "const char const*" deklarieren (ohne Garantie,

Das ist Schwachsinn, denn beide "const" beziehen sich auf das "char".

const und volatile beziehen sich immer auf das Element unmittelbar 
davor, außer wenn sie am Anfang stehen.  Schlaue Leute schreiben 
deswegen der Einheitlichkeit halber "char const *" statt "const char *".

Jürgen

von Juergen (Gast)


Lesenswert?

Ehe ich anfange, einzelne Zugriffe volatile zu casten, nehme ich doch 
lieber gleich eine Memory-Barrier (wie heißt die eigentlich auf 
deutsch?).

Jürgen

von Ferdinand (Gast)


Lesenswert?

Ich habe das hier mal mit verfolgt und hatte gehofft, dass die Lösung 
noch von irgendwem kommt...ich habe nämlich ähnliches.

Zumal das ja auch ne grundsätzliche Sache ist. Kann nicht einer von den 
versierteren Programmieren das Rätsel auflösen?

Da wäre auch ich dankbar.


Ferdinand

von Juergen (Gast)


Lesenswert?

Leider fehlt bei Antons Fragen das Wesentliche, nämlich woher der Leser 
des Puffers weiß, ob bzw., wo und wieviele Daten in dem Puffer sind.

Das einfachste Beispiel wäre ein Byte
1
char Daten;
und ein Flag, ob die Daten gültig sind
1
boolean Daten_gueltig;
Schreiben geht dann so:
1
Daten = ...
2
Daten_gueltig = true;
und lesen
1
if (Daten_gueltig)
2
{
3
    ... = Daten;
4
}

Ohne Optimierung funktioniert das auch so.  Mit Optimierung kommt der 
Kompiler womöglich auf die Idee, die Daten in einer anderen Reihenfolge 
zu lesen.  Z.B. Daten_gueltig wird gesetzt, bevor Daten den korrekten 
Wert hat, also werden dann auch evtl. falsch Daten gelesen.  Oder Werte, 
die einmal in ein Register gelesen wurden, werden nicht nochmal aus dem 
Speicher gelesen (das Warten auf Daten_gueltig in einer While-Schleife 
kann dann etwas länger dauern).

Deklarierst du die Daten jetzt volatile, also
1
char volatile Daten;
2
boolean volatile Daten_gueltig;
dann dürfen die Speicherzugriffe nicht mehr umsortiert oder weggelassen 
werden, es wird also so funktionieren.  Aber es werden auch alle 
Möglichkeiten zur Optimierung bzgl. dieser Variablen verbaut, selbst da, 
wo die genaue Beibehaltung der Zugriffe nicht nötig wäre.

Eine Alternative zu volatile ist es, an den Stellen, wo es auf die 
Reihenfolge ankommt, eine Memory-Barrier zu verwenden:
1
Daten = ...
2
memory_barrier();
3
Daten_gueltig = true;
und lesen
1
if (Daten_gueltig)
2
{
3
    memory_barrier();
4
    ... = Daten;
5
}
Über die Memory-Barrier hinweg dürfen keine Speicherzugriffe verschoben 
werden, das Flag wird also auf jeden Fall nach den Daten geschrieben und 
vor den Daten gelesen.  Wie die Memory-Barrier genau realisiert wird, 
hängt vom Prozessor und vom Kompiler ab.

Bei GCC sollte es das folgende tun (Makro definieren!):
1
    asm volatile ("" : : : "memory");


Jürgen

von Peter D. (peda)


Lesenswert?

Ferdinand schrieb:
> ich habe nämlich ähnliches.

Wenn es nur ähnlich ist, schildere es einfach in einem neuen Thread.

Auch in der Programmierung gibt es keinen Deckel, der auf alle Töpfe 
paßt.

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.