Hallo Leute Es gibt praktisch keine einzige Funktion für Strings, die ich aus C nutzen kann, ohne dass mir der Compiler hunderte von warnings und errors rausschmeist. Ein ganz billiges strlen(demostring) reicht für ein `strlen' discards qualifiers from pointer target type Es muss doch möglich sein unter C einen String auswerten zu können, ohne hunderte von Intergervariablen zur Zeichenzählung mitschleppen zu müssen und jeden String Zeichenweise zu fuss auswerten zu müssen. ... also als ganz einfaches Beispiel und besonders für die ewigen "Kernighand und Ritchie"-Verweiser. Was muss man tun, damit strlen keine warnungen mehr erzeugt. irgendwo habe ich einen thread gefunden der vor kurzem das gleiche Problem hatte nur das PROGMEM bei mir noch mehr Fehler und warnungen erzeugt.
> `strlen' discards qualifiers from pointer target type
Das deutet darauf hin, dass dein char-Pointer entweder const oder
volatile besitzt, strlen aber weniger erwartet. const wird es nicht
sein, da gerade Leute, die mit C oder C++ beginnen, das nur seltenst
benutzen, wenn es nicht absolut notwendig ist (sprich: bei dessen
Fehlen ein Compile-Fehler auftritt). Außerdem wird strlen als Parameter
"const char*" haben.
Also ist es wahrscheinlich volatile. Du hast dann zwei Möglichkeiten:
a) Die Daten des Strings nicht mehr als volatile deklarieren
b) Ein eigenes strlen schreiben, dass "const volatile char*"
übernimmt.
c) Die Warnung mit (const char*)(p) unterdrücken bzw. const_cast<const
char*>(p) in C++ (IIRC).
Nein, ich habe mich nicht verzählt.
a) Kommt in Frage, wenn der String nicht in interrupts o.ä., also
"spontan" aus Compiler-Sicht, modifiziert wird. Vielleicht hast du
den String auch nur falsch deklariert und meinst eigentlich "char *
volatile" anstelle "volatile char*".
b) Würde mehr Code erzeugen
c) Kommt nur in Frage, wenn du 100% absolut sicher ausschließen kannst,
dass der String während des strlen-Aufrufs etwa in Interrupts
modifiziert wird. Sowas solltest du nur mit ausreichenden Kommentaren
einsetzen, da das IMHO eine sehr unsaubere Methode ist.
Falls es kein volatile-Problem ist, muss ich meine Glaskugel wohl mal
wieder in Reparatur geben. Dabei kam die gerade erst zurück.
"Es gibt praktisch keine einzige Funktion für Strings, die ich aus C nutzen kann, ohne dass mir der Compiler hunderte von warnings und errors rausschmeist." Der ist wirklich gut ! Damit kannst Du auch den besten Supportmitarbeiter zur Weißglut treiben. Kein Beispiel mit kompletter Fehlermeldung, nur Wischiwaschi. Sei aber versichert, der Fehler liegt bei Dir. strlen() ist aber ne Funktion, die ich eher selten benutze, die meisten String-Funktionen wissen selber, wo das Ende ist. Bei konstanten Strings ist auch sizeof() besser, da setzt der Compiler gleich die Zahl ein und braucht nicht erst zur Laufzeit abzählen. Peter
> Bei konstanten Strings ist auch sizeof() besser, da setzt der > Compiler gleich die Zahl ein und braucht nicht erst zur Laufzeit > abzählen. strlen() degradiert zu sizeof()-1 bei einem konstanten String. ;-) (Sofern man nicht -ffreestanding benutzt, natürlich.)
"Der ist wirklich gut ! Damit kannst Du auch den besten Supportmitarbeiter zur Weißglut treiben. Kein Beispiel mit kompletter Fehlermeldung, nur Wischiwaschi. Sei aber versichert, der Fehler liegt bei Dir." @PeDa Da braucht man sich nicht wundern, wenn dann sowas passiert. http://www.mikrocontroller.net/forum/read-7-328980.html
@xXx Der Einwand des Ursprungsposters war: "Es gibt praktisch keine einzige Funktion für Strings, die ich aus C nutzen kann, ohne dass mir der Compiler hunderte von warnings und errors rausschmeist." In der Annahme daß "der Compiler" (welchen auch immer er nutzte) nicht total buggy ist, ist Peter Dannegers "Sei aber versichert, der Fehler liegt bei Dir." meines Erachtens sachlich korrekt und vollständig gerechtfertigt. Die Frage ist halt, wie kann man dem Ursprungsposter unterstützen in der Identifikation was da schief läuft? Da sollte sich meines Erachtens der Ursprungsposter besser in einer defensiven Rolle bewegen als in einer offensiven ("blanker Hass", ... "es muß doch möglich sein"..). Und wie man in den Wald reinruft, so schallt es heraus. PS: Christophs Antwort auf die "Frage" des Ursprungsposters halte ich aber ehrlich auch entspannender als Peters Breitseite...
Hi, PeDa weisst nur nochmal dringlicher daraufhin genaue Angaben zum Problem zugeben. Ohne konkrete Angaben z.B. Fehlermeldung und Sourcecode kann man immer nur stochern und man muss stundenlang den Threadoeffner die Informationen aus der Nase poppeln. Bitte achtet darauf soviele Informationen zugeben wie es moeglich ist. Lieber ein paar Zeilen mehr posten als Sie zuverstecken. Gruß, Dirk
erst mal danke an alle die die Glaskugel bemüht haben. Ich war mir nicht im klaren darüber das die Frage so ungenau ist... Das der Fehler bei mir liegt ist schon klar... nur wo... Es ist in der Tat ein volatile char und soll ein Zeichen von UART-Interrupt angehangen bekommen... Bei genauerem überlegen ist das Argument der unsauberen Programmierung berechtigt, wenn die Interruptroutine fröhlich Zeichen in einen String reinschreibt während der gerade in Bearbeitung ist... Da ist wohl noch etwas Arbeit am Quellcode von Nöten ...
@xXx, daß Du anonym bleibst, ebenso, wie der Autor Deines Links (bist Du es selber ?), spricht nicht gerade für Dich. Ich stehe jedenfalls zu meinen Worten. Und ich finde es grob unhöflich, wenn man Ergüsse seiner vagen Vermutungen plaziert, ohne dem Leser die Möglichkeit zu geben, sie auch nachzuvollziehen. Daher war ich so freundlich, denjenigen darauf hinzuweise, daß es dem Posting an Grundlegendem mangelt. Obendrein habe ich noch ausgeführt, daß viele Programmierer die Funktionen erfolgreich nutzen, also der Fehler nicht im Compiler fußt. Es sind also alles nur freundlich gemeinte Hinweise, sein Programm bzw. seine Frageweise nochmal zu überdenken. Peter
@flyingwolf, "Bei genauerem überlegen ist das Argument der unsauberen Programmierung berechtigt, wenn die Interruptroutine fröhlich Zeichen in einen String reinschreibt während der gerade in Bearbeitung ist..." So unsauber ist das garnicht. Ich benutze auch einen UART-Puffer, den ich auswerte, wärend schon fleißig die nächsten Bytes reinkommen können. Wenn das sauber programmiert ist (kritische Stellen mit Interruptsperre kapseln), klappt das prima. Ansonsten wären das auch nur logische Fehler, die der Compiler nicht erkennen kann und daher nicht anmeckert. Dein Problem dürfte also woanders liegen. Peter
das geht extern volatile char instring[100], stringend; // extern volatile int stringcounter; SIGNAL(SIG_UART0_RECV) { serial_in = UDR0; strcat((char*)&instring,(char*)&serial_in); if (serial_in == 0x0d) stringend =1; } das geht nicht extern volatile char instring[100], stringend; // extern volatile int stringcounter; SIGNAL(SIG_UART0_RECV) { serial_in = UDR0; strcat(instring,serial_in); if (serial_in == 0x0d) stringend =1; } Christoph hat es, glaube ich, ganz gut auf den Punkt gebracht, wenn ich auch nur Fragmente davon verstanden habe.. Aber in einer ruhigen minute werde ich mich mal hinsetzen und versuchen rauszubekommen, was der Hinergrund des Ganzen ist
> das geht > strcat((char*)&instring,(char*)&serial_in); Nein, es maskiert das Problem nur. Der Typecast sagt dem Compiler: ,,Lieber Compiler, von dem, was ich hier schreibe, weiß ich genau, was ich gerade mache.'' Eigentlich weißt du's aber wohl eher nicht. ;-) Lass doch das volatile am Zeichen-Puffer einfach weg. Der Code wird nicht besser und nicht schlechter damit. Wenn du ein volatile brauchst, dann irgendwo an einer Semaphore-Variablen im Sinne dessen, was Peter geschrieben hat, damit Interruptservice und main thread im Konfliktfall gegeneinander verriegelt werden.
@flyingwolf: Warum benutzt Du volatile eigentlich? Volatile ist dafür da, damit der compiler es nicht weg optimized. versuch mal folgendes: /* Start of code */ extern char instring[100]; extern int stringcounter; char serial_in; SIGNAL(SIG_UART0_RECV) { serial_in = UDR0; strncat( (char *)&instring, (char *)&serial_in, 1); if (serial_in == 0x0d) stringend =1; } /*end of code */ Ich würde strncat nehmen, weil du nur ein char an ein string kleben möchtest, oder nicht. Mit strcat würdest Du einen string an instring kleben, also einen zeichen reihe der mit 0x00 eindet. Ich würde auch noch testen ob ich noch platz im instring buffer habe, sonst landen die zeichen irgendwo anders, die variable die nach instring im specher steht. Dies ist ein oft gemachte fehler. Grüße Mark,
>> Wenn das sauber programmiert ist (kritische Stellen mit >> Interruptsperre kapseln), klappt das prima... naja, manchmal geht das aber besser man verwendet lock-free algorithmen
>> das geht >> strcat((char*)&instring,(char*)&serial_in); > > Nein, es maskiert das Problem nur. Der Typecast sagt dem Compiler: > ,,Lieber Compiler, von dem, was ich hier schreibe, weiß ich genau, > was ich gerade mache.'' Eigentlich weißt du's aber wohl eher > nicht. ;-) (gedacht fuer flyingwolf, Jörg weiss das eh alles) Er weiss es sogfar mit Sicherheit nicht. Auch wenn er die Deklaration von serial_in nicht gezeigt hat, so kann man doch davon ausgehen, dass dies ein einzelner char ist. Also sowas: char serial_in; oder meinetwegen auch volatile char serial_in; Egal welche Version, es ist in jedem Fall falsch. Den strcat erwartet als zweites Argument nun mal kein einzelnes char, sondern einen Pointer auf ein Array von Charactern, in denen ein String (also eine Zeichenkette die mit '\0' abgeschlossen ist) abgelegt ist. Und die ist nun mal nicht da. Daher wird strcat() da auf Teufel komm raus versuchen an instring Zeichen anzuhaengen. Je nachdem was sich da noch zusaetzlich im Speicher abspielt ist es ohne weiters moeglich, dass instring kraeftig ueberlaufen wird und du dir ne Menge anderen Speicher zerschiesst. Fazit: Die String-Funktionen (die str.. Familie) sind eigentlich sehr einfach zu benutzen. Nur muss man ein paar Grundlagen kennen. Beachtet man diese nicht, dann ist das Ergebnis frustrierend, da mann auf keinen grünen Zweig kommt. Woher hat man diese Grundlagen: Literaturstudium.
@jörg
>>Lass doch das volatile am Zeichen-Puffer einfach weg.
ich habe mal gelesen, (hier im Forum) das Variablen, die in
Interruptroutinen verknotet werden eine volatile davor bekommen, damit
sie die Interruptroutine nicht hinterrücks verbiegen kann ohne dass das
laufende Pragramm davon was mitbekommt.
Ehrlich gesagt schleppe ich die volatile eigentlich nur noch aus einem
Denkfehler heraus mit mir rum. Ich hatte vorher nur das Zeichen
übergeben und schon während des Empfangs verarbeitet (braucht weniger
speicher) damit das Zeichen nicht doppelt verarbeitet wurde, musste es
nach der Verarbeitung 0x00 gesetzt werden...
zur Optimierung der Geschichte werde ich mir die Vorschläge hier alle
jedenfalls mal zu Gemüte führen und ausprobieren ...
@pittbull_nt@yahoo.com "naja, manchmal geht das aber besser man verwendet lock-free algorithmen" Das geht aber eben nicht immer. Ich verwende für die UART immer einen linear-Puffer, dann kann ich gleich den Parser über den Puffer jagen und muß nicht doppelten Speicherplatz belegen und umständlich umkopieren. Am Ende des Kommandos muß ich nur das Kommado entfernen, d.h. alle zwischenzeitlich empfangenen Bytes an den Anfang verschieben. Und dieses Verschieben muß unter UART-Interruptsperre erfolgen, sonst knallt mir die UART die Zeichen sonstwohin. Peter
Leute, was ihr hier mit den Buffern in den Interrupt-Funktionen macht, führt früher oder später zu Bugs. Wichtige Regel (ohne Ausnahme!): Werden globale Variablen von Interrupt-Funktionen gelesen oder gerschrieben, so müssen diese Variablen 'volatile' deklariert werden. Es gibt keine Ausnahme. Und das hat natürlich auch zur Folge, dass stdlib-Funktionen nicht verwendet werden dürfen. Denn die wissen ja nicht dass sie auf einen volatile-Buffer arbeiten sollen. Der Compiler warnt also vollkommen korrekt und der Compiler darf in diesem Fall falschen Code produzieren. Die Lösung ist aber auch nicht weiter kompliziert, und sieht ungefähr so aus: #define BUFFER_SIZE 100 volatile char buffer[BUFFER_SIZE]; volatile char volatile * buffer_next = buffer; int is_buffer_empty() { return buffer_next == buffer; } int is_buffer_full() { return buffer_next == buffer + sizeof(buffer); } int buffer_length() { return buffer_next - buffer; } int buffer_putc(char c) { if (is_buffer_full()) return -1; *buffer_next++ = c; return 0; } char buffer_getc() { if (is_buffer_empty()) return 0; char c = *buffer; for(char volatile * p = buffer + 1; p < buffer_next; p++) *(p-1) = *p; buffer_next--; return c; }
Achso, buffer_clear() ist natürlich auch trivial: void buffer_clear() { buffer_next = buffer; } Und anstatt der 'int' für die is_buffer_xyz() ist es auf Mikrocontroller besser 'char' zu verwenden. Dann kann man noch Teile als 'static' und/oder 'inline' deklarieren. Wie gesagt, das ist nur das Gerüst. Und keine Angst, das doppelte 'volatile' usw. ist alles korrekt. ;-)
> Wichtige Regel (ohne Ausnahme!): > Werden globale Variablen von Interrupt-Funktionen gelesen oder > gerschrieben, so müssen diese Variablen 'volatile' deklariert > werden. Das halte ich für eine Übertreibung. Der Compiler ist sehr wohl daran gebunden, die Werte globaler Variablen irgendwann zurückzuschreiben. Notgedrungen muss er das spätestens am Ende einer (nicht inline implementierten) Funktion erledigt haben, d. h. beim Verlassen der ISR muss der globale Puffer mit den empfangenen Zeichen aktualisiert worden sein. Der auswertende Teil darf sich zwar die gelesenen Werte cachen (der Standard schreibt, er darf davon ausgehen, dass sie sich außerhalb seines Codes die Werte nicht ändern), aber irgendwann muss auch er sich die Initialwerte erst einmal aus dem Speicher gelesen haben. Wenn man nun also erreichte, dass durch eine (sehr wohl `volatile' deklarierte) Semaphor-Variable sichergestellt ist, dass Aktion 1 (vollständiges Schreiben der in der ISR gelesenen Daten in das globale Array) vor Aktion 2 (Beginn des ersten Auslesens aus dem globalen Array) erfolgt, kann man durchaus ohne den übertriebenen Aufwand deines Vorschlags auskommen. Schließlich hamm wir's hier nicht dicke, wir arbeiten auf einem Microcontroller, auf dem Ressourcen knapp sind. Wiederbenutzung von RAM-Puffern ohne unnützes Umkopieren ist daher in vielen Fällen eine grundlegende Forderung (sRAM ist der teuerste Teil der heutigen Microcontroller).
> Und keine Angst, das doppelte 'volatile' usw. ist alles korrekt. ;-) > [...] > volatile char volatile * buffer_next Mag sein, dass ich mich irre, weil ich schon einige Zeit nicht mehr mit solch rohen Zeigern programmiert habe, aber meiner Meinung nach ist das zweite volatile an der falschen Stelle; es sollte hinter dem * sein, damit der Zeiger selbst volatile ist: volatile char * volatile buffer_next Außerdem hast du immer noch eine race condition drin, wenn ich mich nicht täusche. Angenommen, der Puffer enthält genau zwei Zeichen, buffer_next ist also buffer + 2 (sei der Puffer "AB"). Angenommen, es wurde gerade die Schleife in buffer_getc() ausgeführt. Da buffer_next == buffer + 2 ist, wurde die Schleife exakt ein Mal durchlaufen, die ersten beiden Zeichen des Puffers sehen nun also so aus: "BB". Natürlich ist nur das erste 'B' gültig, aber da buffer_next noch nicht erniedrigt wurde, enthält es zu diesem Zeitpunkt immer noch die Information "buffer + 2". Nun tritt ein Interrupt auf, buffer_putc('C') wird aufgerufen. Was jetzt passiert, ist denke ich klar. buffer_putc schreibt das Zeichen an die Stelle *(buffer+2), sodass der Puffer nun "BBC" enthält. Dann wird buffer_next um eins erhöht, Interrupt kehrt zurück, buffer_getc wird weiter ausgeführt, buffer_next wird wieder um eins erniedrigt. Der Puffer enthält nun "BBC", buffer_next behauptet, die ersten beiden Zeichen wären gültig, das heißt buffer_getc wird nacheinander 'B', 'B' und dann nur noch 0 liefern.
Die volatile Semaphor-Veriable wird aber überhaupt nix helfen. Das Lesen oder Schreiben einer volatile-Variable "syncronsisiert" den Speicher eben nicht. Andere Teile oder andere Variablen einer Funktion können dennoch mit lokalen Kopien der globalen Variablen arbeiten. Es ist richtig, dass die Funktion irgendwann die globalen Variablen lesen oder schreiben musst. Aber der Compiler kann das machen, wie er lustig ist. Gerade Dein Beispiel mit den statisch Funktionen ist ein gutes Beispiel. Statische Funktionen bestimmter Maximalgröße oder die nur von einer Stelle aus aufgerufen werden, kann der Compiler auch völlig korrekt selbst 'inline' erzeugen. Dann kommen auch noch solche Geschichten wie Tail-Calls und umsortieren der Statements usw. dazu. Der springende Punkt ist der, dass Du mit der Semaphor-Variable das Verhalten des Compilers bezüglich Nicht-Semaphor-Variablen in keinster Weise beeinflussen kannst.
@Christoph: Wegen Race-Condition: Natürlich hast Du recht. Die Interrupts müssen korrekt gesperrt sein. Das müssen sie immer. Es gibt in C eben kein atomares Read-Modify-Write. Mit dem volatile-Schlüsselwort könntest Du auch recht haben. Mich haben auch Zweifel beschlichen, als ich auf "Submit" geklickt habe. Also das müsste man wirklich nochmals nachlesen...
> Also das müsste man wirklich nochmals nachlesen...
Im Grunde ist es ganhz einfach. volatile ist ein 'modifier'
so wie auch 'const'.
Und fuer die gilt: sie wirken immer auf das was links von
ihnen steht. Es sei denn, da ist nichts mehr, dann wirken
sie auf das was rechts von ihnen steht.
Also:
char const a; a ist ein konstanter character
const char a; a ist ein konstanter character
char const * a; a ist ein pointer auf einen const character
const char * a; a ist ein pointer auf einen const character
char * const a; a ist ein Pointer. Dieser Pointer ist const
und zeigt auf einen character
char const * const a; a ist ein Pointer. Dieser Pointer ist
const und zeigt auf einen const character
const char * const a; a ist ein Pointer. Dieser Pointer ist
const und zeigt auf einen char, der selbst
wieder const ist.
const char const * a; a ist ein Pointer. Der Pointer zeigt
auf etwas das const ist. Naemlich einen
char, der const ist.
(Hier hast Du also 2-mal dasselbe aus-
gedrueckt. Naemlich dass der char als
const anzusehen ist).
Selbiges fuer volatile, bzw. Kombinationen aus const und volatile.
Vereinfacht ausgedrueckt: Fang beim Variablennamen zu lesen an
und arbeite dich nach links durch.
Ich sehe das genauso wie Jörg. Wenn ich eine Funktion getchar() oder get_command() habe, dann muß diese den Pufferzeiger einlesen und auswerten ganz ohne volatile. Und nur die Funktion clear_command() macht eine Interruptsperre, wärend sie das nächste Kommando wieder an den Anfang des Puffers kopiert. Wenn ein Interrupthandler viel mit einer Variable hantiert, kann es sogar effektiver sein, dies nicht als volatile zu deklarieren und dafür eine extra Funktion zum Auslesen zu nehmen. Diese Funktion darf dann natürlich nicht inline wegoptimiert werden. Peter
@unbekanter: was meinst Du mit: "Die volatile Semaphor-Veriable wird aber überhaupt nix helfen." Das volatile keyword sorgt nur dafür das der compiler denn code nicht optimiert, weil es durch externe einflusse geändert werden kann. beispiel 1: volatile unsigned char *baseAddr; lsb = *handle->baseAddr; middle = *handle->baseAddr; msb = *handle->baseAddr; kann ohne das volatile optimiert werden zu: lsb = middle = msb = *handle->baseAddr; mit volatile werde alle drei zuweizungen komplett aufgeführt. beispiel 2: volatile int x; for(x=0; x<100; y++); ohne volatile kann der compiler denken das der code keine funktion hat, wenn "x" nicht weiter benutzt wird, und die schleifen raus-optimieren. Ansonsten mal suchen nach "volatile" bei google. Grüße Mark, Grüße Mark,
@Karl-Heinz Danke für deinen Beitrag. Ich hatte nämlich immer Probleme mir zu Merken worauf welches const/volatile denn nun wirkt. Eigenentlich ganz einfach. Ich glaube ich persöhnlich werde in Zukunft vermeiden, rechts der Typen den "modifier" zu schreiben. (habe ich bisher immer gemacht) Dann ist die Logic sofort klar...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.