Guten Tag Ich habe ein Problem mit Variablen, die den Wert ändern (Atmega16, gcc) Ich habe einen Array, in den ich Werte meiner DCF77-Uhr (Byte für Byte) einschreibe, sowie ich sie via TWI gelesen habe. Diese Werte möchte ich in einer anderen Funktion in einem andern File verwenden. Das funktioniert aber nur beim ersten Mal, dann haben die Elemente des Arrays andere, beliebige Werte. Also: uint8_t DCF77daten[8]; main ... Uhr lesen ... for i... { DCF77daten[i]=buffer[i]; } //der buffer ist als volatile uint8_t[8] deklariert Hier kommen die Daten richtig ins Array In display.c: .. uint8_t Minute=DCF77daten[0]; usw. .. Hier sind die Daten beim ersten Mal richtig, nachher nicht mehr. Sie werden auch nicht aktualisiert, wenn in main die Uhr wieder gelesen wird. Auch wenn ich in main einfür DCF77daten[0] direkt 3 einsetze, wird der Wert verändert. Wenn ich anstelle eines Array ein struct nehme und die Werte dort einsetze, arbeitet das Programm korrekt. Was mache ich da falsch? Ruedi
> Was mache ich da falsch?
Auf der Basis der paar verstümmelten Codezeilen kann dir das hier
niemand sagen.
Was mir jetzt komisch vorkommt, ist die Deklarierung in
1 | .. |
2 | uint8_t Minute=DCF77daten[0]; usw. |
3 | .. |
Wenn es das nicht ist, wäre vermutlich etwas mehr Code hilfreich. Wenn du möchtest, dass sich Minute automatisch immer auf den Wert von DCF77daten[0] stellt, wäre ein Pointer angebracht. Gruß, CowZ
Bei den Codeschnipseln würde mir nur meine Kristallkugel weiterhelfen, die ist aber leider gerade zum Nachjustieren beim Hersteller. Wo hast du zum Beispiel Minute deklariert? In einer Funktion? Global? Dito für DCFDaten. Wie hast du den Zugriff darauf aus zwei verschiedenen Sourcedateien programmiert? So erkenne ich gar nix, erst recht keinen Fehler.
Vielen Dank für die sonntäglichen Antworten, und sorry, dass ich etwas zuviel gespart habe. Also etwas mehr vom Code: vor 'main': //In diesen Buffer schreiben die verschiedenen TWI-Slaves ihre Daten, wenn sie aufgerufen //werden. volatile uint8_t buffer[8]; // globale Variable für die Daten der Uhr. uint8_t DCF77daten[8]; //Flag, in dem von einem Timer Bits gesetzt werden, die bestimmen, welcher Slave aufgerufen //werden soll: uint8_t TWI_Flag=0; main ... if (TWI_Flag & (1<<UHR_Bit)) { SlaveLesen(DCF77_Adresse); //TWI-Funktion nach P. Fleury TWI_Flag &= ~(1<<UHR_Bit); //Daten in Array schreiben: uint8_t i; for (i=0;i<8;i++) { DCF77daten[i]=buffer[i]; } } //Hier kommen die Daten richtig ins Array (Kontrolle in LCD) //Im File 'display.c' werden die Daten ins LCD gesetzt: display.c: void display() { uint8_t Minute=DCF77daten[0]; // Nach diesem Aufruf sind die Daten in DCF77daten verändert. lcd_gotoxy(0,3); lcd_puthex(Minute); //dto für Stunde und Wochentag .. } Bei jedem Timeraufruf wird die Uhr gelesen, die Daten in DCF77daten eingesetzt und die Funktion 'display() aufgerufen. Die falschen Daten werden aber nicht aktualisiert. Und eben: Wenn ich anstelle eines Array ein struct nehme und die Werte dort einsetze, arbeitet das Programm korrekt. C ist für mich relativ neu (Pascal, c++, Objectiv-C auf OSX), darum vermute ich einen grundsätzlichen Fehler mit volatile und globalen Variablen. Das Programm ist sehr umfangreich und läuft 'sonst' eigentlich korrekt. Ruedi
Erst einmal wundert es mich, dass TWI_Flag nicht volatile ist. Das nur am Rande. Du hast aber immer noch nicht angegeben, wie du von display.c auf DCF77daten zugreifst. Was steht in der Header-Datei? An "extern" gedacht?
Momoll, TWI_Flag ist schon volatile. Falsch abgeschrieben. DCF77daten ist in display.c als extern volatile uint8_t DCF77daten[] deklariert. Wenn ich es weglasse, passiert nichts.
Was heißt "passiert nichts". Lässt es sich nach wie vor compilieren oder gibt es eine Fehlermeldung?
Lässt sich nach wie vor kompilieren und gibt gleiche - veränderte - Resultate aus.
Ruedi Heimlicher wrote: > Lässt sich nach wie vor kompilieren und gibt gleiche - veränderte - > Resultate aus. Und das gibt dir nicht zu denken? Irgendwo muss die Variable doch deklariert sein, sonst gäbe es eine Fahlermeldung vom Compiler. Irgendwo muss also noch eine Deklaration sein. Ich vermute einmal stark, dass in main.c und display.c jeweils ein Feld mit diesem Namen deklariert wird. Das sind dann zwei vollkommen separate Variablen und daher hat das Feld in display.c auch einen anderen Inhalt als in main.c.
Detlev T. wrote: > Ich vermute einmal stark, dass in main.c und display.c jeweils ein Feld > mit diesem Namen deklariert wird. Das sind dann zwei vollkommen separate > Variablen und daher hat das Feld in display.c auch einen anderen Inhalt > als in main.c. Nein, nicht beim gcc. Doppelt definierte Variablen legt der Linker stillschweigend auf gleiche Adressen.
@ Ruedi Heimlicher: > // Nach diesem Aufruf sind die Daten in DCF77daten verändert. > ... > Das Programm ist sehr umfangreich ... Sieht so aus, als ob der Stack mit den globalen Variablen kollidiert. > Wenn ich anstelle eines Array ein struct nehme und die Werte dort > einsetze, arbeitet das Programm korrekt. Weil die Variablen dann vermutlich in einer anderen Reihenfolge im RAM liegen. Es werden dann einfach andere Variablen überschrieben und es scheint dann zu funktionieren.
Stefan Ernst wrote: > Nein, nicht beim gcc. Doppelt definierte Variablen legt der Linker > stillschweigend auf gleiche Adressen. Stimmt, habe ich ausgetestet. Wieder etwas dazu gelernt. Aber ist denn so ein Verhalten ANSI-C konform?
//uint8_t DCF77daten[8]; würde ich mal als volatile deklarieren. Ansonsten vermute ich auch eine Stack-Kollision.
Detlev T. wrote: > Aber ist denn > so ein Verhalten ANSI-C konform? Zumindest ist es ISO-C-konform. ;-) Ist in «Annex J.5.11 Multiple external definitions» im Standard explizit als mögliche Erweiterung aufgeführt und unter «6.2.2 Linkages of identifiers» im C99 Rationale diskutiert. Allerdings ist ein Programm, das sich auf dieses Feature verlässt, zwar konform innerhalb seiner Umgebung, aber nicht "maximally portable", wie es im Rationale genannt wird.
Was passiert denn wenn man 2 Dateien includiert, in denen nur zufällig 2 Variablen mit gleichem Namen definiert werden? Ich hab es bis jetzt immer für korrekt gehalten, wenn dann wirklich auf 2 verschiedene Variablen zugegriffen wird. Denn woher hat der Linker die Gewissheit, dass der Programmierer wirklich die selber Variable meint? Ich finde das is wieder so ein Fall, wo sich ein Tool unzulässig in die Entscheidungen des Anwenders einmischt. Wenn man die selbe Variable meint, sollte man das mit extern auch so schreiben müsses. Ansonsten sollten es 2 Variablen sein. Nur so macht der Linker schließlich garantiert exakt das, was ihm der Programmierer mit seinem Quelltext sagt.
> Was passiert denn wenn man 2 Dateien includiert, in denen nur zufällig 2 > Variablen mit gleichem Namen definiert werden? Wenn die Variablen nur innerhalb der Dateien eine Bedeutung haben, sollten sie, wenn man sauber programmiert, als "static" deklariert sein. Dann bekommt sie der Linker gar nicht erst zu Gesicht.
Rufus t. Firefly wrote: > ... als "static" deklariert sein. > Dann bekommt sie der Linker gar nicht erst zu Gesicht. Echt? Wer entscheidet dann, wo im Speicher diese Variablen zu liegen kommen?
@ Klaus: Entweder werden diese beiden Variablen nur in ihren jeweiligen Modulen gebraucht, dann sollten sie static sein (wie Rufus schon gesagt hat), dann gibt es kein Problem. Oder ein externer Zugriff sollte/muss möglich sein, dann ist dein Vorschlag, daraus zwei getrennte Variablen zu machen, Kokolores. Welche der beiden soll denn dann genommen werden, wenn ein externer Zugriff auf den "gemeinsamen" Namen erfolgt? Die einzige sinnvolle Alternative zum aktuellen gcc-Linker-Verhalten, die ich sehe, wäre Abbruch mit Fehlermeldung.
hm, ja irgendwie hast du Recht :) Wobei ich dann immer noch für die Fehlermeldung (oder wenigstens ein warning) bin, dann weiß der Programmierer wenigstens das da möglicherweise was nicht läuft wie es soll.
Klaus wrote: > Wobei ich dann immer noch für die Fehlermeldung (oder wenigstens ein > warning) bin, dann weiß der Programmierer wenigstens das da > möglicherweise was nicht läuft wie es soll. Ich habe gerade mal einen Blick in die Dokumentation geworfen. Ich denke die Linker-Option --warn-common macht genau das.
Stefan Ernst wrote: >> ... als "static" deklariert sein. >> Dann bekommt sie der Linker gar nicht erst zu Gesicht. > > Echt? Wer entscheidet dann, wo im Speicher diese Variablen zu liegen > kommen? Das entscheidet natürlich schon der Linker :), allerdings sieht er zwei verschiedene Namen dafür. Sie heißen auf globaler Ebene nicht mehr 1:1 so, wie sie im C-Programm heißen. Man kann übrigens auch den GCC dazu bewegen, dieses (eher historische) Verhalten nicht anzuwenden, indem man mit -fno-common übersetzt. Dann legt er die .bss-Variablen nicht als sogenannte COMMON-Blöcke an (wer FORTRAN noch kennt weiß, woher dieser Name und das damit verbundene Feature des Linkers stammen), sondern legt sie explizit in der .bss- Section als Symbole an. In diesem Falle meckert der Linker dann sehr wohl, wenn man die entsprechenden Symbole doppelt definiert.
> Echt? Wer entscheidet dann, wo im Speicher diese Variablen zu liegen > kommen? Lokale Variablen eines Moduls werden AFAIK zu einem Block zusammengefasst, der vom Linker ohne weitere Analyse zur Verfügung gestellt wird. Das entscheidende ist, daß auf Objektdateiebene statische Variablen keinen Symbolnamen erhalten, den der Linker versuchen könnte aufzulösen. Das gcc-Verhalten, das sich jetzt anscheinend (wenn ich Jörgs Beitrag zu ISO-C99 richtig interpretiere) jetzt sogar in der Norm einschleicht, halte ich hingegen nach wie vor für einen schweren Fehler. Die Linkeroption --warn-common sollte prinzipiell und immer aktiviert werden. Jetzt könnte man noch einen Betriebssystemstreit lostreten:
1 | It seems to be a common practice in the unix world. |
2 | According to ld manual : |
3 | |
4 | --warn-common |
5 | |
6 | Warn when a common symbol is combined with another common symbol |
7 | or with a symbol definition. Unix linkers allow this somewhat |
8 | sloppy practice, but linkers on some other operating systems do |
9 | not. This option allows you to find potential problems from com- |
10 | bining global symbols. Unfortunately, some C libraries use this |
11 | practice, so you may get some warnings about symbols in the |
12 | libraries as well as in your programs. |
Rufus t. Firefly wrote: > Das gcc-Verhalten, das sich jetzt anscheinend (wenn ich Jörgs Beitrag zu > ISO-C99 richtig interpretiere) jetzt sogar in der Norm einschleicht, > halte ich hingegen nach wie vor für einen schweren Fehler. Das schleicht sich nicht jetzt erst ein, sondern war schon immer da. Das Rationale dokumentiert, dass man sich bei der Normierung von ANSI C89 nicht normierend für eine der drei möglichen Varianten entscheiden konnte und daher die Norm so gestaltet hat, dass sie alle zulässig sind. Eine solche Vorgehensweise ist nicht weiter verwunderlich, schließlich können und wollen solche Normungsgremien ja nicht alle existierenden Implementierungen auf einmal auf die Seite "nicht konform" schieben, und diese Implementierungsvariante war nun einmal auf Unix-Plattformen für mehr als ein Jahrzehnt gang und gäbe. Ich vermute mal, dass das Ur-C einfach noch gar kein "extern"- Schlüsselwort besaß, und dass das entsprechende Verhalten daher kommt. Auch war die Benutzung von COMMON-Blöcken gängige Praxis in der FORTRAN-Zeit, sodass es nicht verwundert, dass C auf diese Weise ein Pendant dazu geschaffen hat. Wie geschrieben, man braucht das nicht einmal nur als Warnung zu nehmen, man kann es mit -fno-common auch komplett abschalten im GCC. Ah, im GCC-Handbuch gibt es auch noch einen Hinweis. C++ besitzt die Option -fconserve-space, die gleiches Verhalten wie be C (default) erreicht. Die Beschreibung endet mit dem Satz: “This option is no longer useful on most targets, now that support has been added for putting variables into BSS without making them common.” Offenbar war das mit dem BSS also noch nicht immer so.
> Ich vermute mal, dass das Ur-C einfach noch gar kein "extern"- > Schlüsselwort besaß, und dass das entsprechende Verhalten daher > kommt. Da hast Du vermutlich recht. Ur-C war grauenhaft. Muss ich mal in meinen Ur-K&R schauen, brr. Ja, so einen habe ich noch, in der furchtbaren deutschen Übersetzung und dem sehr drolligen Satz (Wörter wie pointer variable wurden übersetzt als "Zeiger Variable" und Codebeispiele wurden prinzipiell in Proportionalschrift abgedruckt).
Nee, der K&R ist dafür schon zu modern, da gab es schon Dinge wie void und auch extern. Meine Vermutung bezieht sich auf ca. noch 10 Jahre davor.
>meinen Ur-K&R schauen, brr...
Aber das Beispiel zur Rekursion ist doch ein Klassiker...
(Wers noch nicht kennt, der suche mal in seinem K&R hinten im
Stichwortverzeichnis nach "Rekursion")
Und das Variablen ihren Wert ändern liegt ja schon in ihrer Natur ;-)
(Kann aber schon mal auch an freilaufenden Pointern/Zeigern liegen)
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.