Forum: Compiler & IDEs Variablen verändern Wert


von Ruedi H. (Firma: prim) (ruediheimlicher)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

> Was mache ich da falsch?

Auf der Basis der paar verstümmelten Codezeilen kann dir das hier 
niemand sagen.

von CowZ (Gast)


Lesenswert?

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

von Detlev T. (detlevt)


Lesenswert?

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.

von Ruedi H. (Firma: prim) (ruediheimlicher)


Lesenswert?

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

von Detlev T. (detlevt)


Lesenswert?

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?

von Ruedi H. (Firma: prim) (ruediheimlicher)


Lesenswert?

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.

von Detlev T. (detlevt)


Lesenswert?

Was heißt "passiert nichts". Lässt es sich nach wie vor compilieren oder 
gibt es eine Fehlermeldung?

von Ruedi H. (Firma: prim) (ruediheimlicher)


Lesenswert?

Lässt sich nach wie vor kompilieren und gibt gleiche - veränderte - 
Resultate aus.

von Detlev T. (detlevt)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

@ 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.

von Detlev T. (detlevt)


Lesenswert?

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?

von Tobi (Gast)


Lesenswert?

//uint8_t DCF77daten[8];

würde ich mal als volatile deklarieren. Ansonsten vermute ich auch eine 
Stack-Kollision.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Klaus (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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?

von Stefan E. (sternst)


Lesenswert?

@ 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.

von Klaus (Gast)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>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
Noch kein Account? Hier anmelden.