Variablen verändern Wert

OP (Firma: prim) #931754
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
Gast #931806
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
#931820
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.
OP (Firma: prim) #931871
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
#932053
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.
#932099
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.
#932109
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.
Moderator Persönliche Seite #932670
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.
Gast #932943
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.
#932980
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.
Gast #932997
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.
#933017
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.
Moderator Persönliche Seite #933020
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.
Persönliche Seite #933027
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.
Moderator Persönliche Seite #933329
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.
Persönliche Seite #933378
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).
Moderator (Firma: Titel) Persönliche Seite #933455
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)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren