Forum: Mikrocontroller und Digitale Elektronik eine mit 'volatile' erzeugte Variable wird im zweiten C-File ignoriert


von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Hallo Forengemeinde,

Habe ein Atmega32 Projekt, wo ich bis jetzt folgende Files habe:

- ein Include-File wo ein Teil meiner Variablen deklariert sind.
- ein Main-C File, wo meine Haupt-Anwendung steckt, mit ca. 500 Zeilen 
Code.
- ein C-File, wo meine Service-Routinen stecken, mit ca. 400 Zeilen 
Code.

In beiden C-Files ist die Include-Datei, und weitere eingebunden.

In der Main-C steckt eine Timer/ISR Routine, die ich für das Display und 
andere Sachen, wie Sekunden-Timer, ect. benötige.
Diverse Variablen, wie z.B. Chip_Timeout sind mit 'volatile' erstellt. 
Erst in der Main, dann verschoben in der Header mit dem Zusatz 'EXTERN' 
. Also sollte ich doch von ALLEN C-Files darauf zugreifen können.

- EXTERN volatile int Chip_Timeout;

Soweit funktioniert auch die Anwendung, inclusive Hardware.

Nun zum Problem:
Chip_Timeout funktioniert auch in der Main.c, da hier die ISR ist.

Ich benötige aber diesen Chip_Timeout aber auch in dem C-File, wo mein 
'Service' - Zeugs ist. Und da klappt es nicht. Es kommt kein Fehler beim 
Build. Anscheinend wird die Variable (Chip_Timeout) in der ISR 
ignoriert, oder was ???

Wie kann man diese Problem lösen, damit Chip_Timout in beiden C-Files 
funktioniert ?

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas S. schrieb:
> Wie kann man diese Problem lösen, damit Chip_Timout in beiden C-Files
> funktioniert ?

Mit einem nachvollziehbaren Codebeispiel.

Sorry, die Prosa allein genügt bei sowas nicht. Falls du beispielsweise 
EXTERN wirklich so geschrieben hast - das gibt es in C nicht als 
Schlüsselwort …

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> dann verschoben in der Header mit dem Zusatz 'EXTERN'

Damit hast du aus einer Definition eine Deklaration gemacht.

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Jörg W. schrieb:
> Falls du beispielsweise
> EXTERN wirklich so geschrieben hast - das gibt es in C nicht als
> Schlüsselwort …

Habe ich so aus einem Beispiel, evtl. sogar vom Tutorial hier. Findes es 
aber gerade nicht. Von irgendwo, mit GCC her. Da war es so geschrieben.

Wie würde es denn richtig sein?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> Wie würde es denn richtig sein?
extern

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Arduino F. schrieb:
> Thomas S. schrieb:
>> Wie würde es denn richtig sein?
> extern

Habs grad getestet.
Schreibe ich es :extern - dann ist es zwar blau und wäre Schlüsselwort. 
Aber es kommen dann unzählige Fehler.

EXTERN geschrieben ist es schwarz. Aber der Compiler meckert nicht, und 
es funktioniert.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> Aber es kommen dann unzählige Fehler.

Beheben!

Thomas S. schrieb:
> :extern

extern und nicht :extern

: Bearbeitet durch User
von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Arduino F. schrieb:
> Thomas S. schrieb:
>> Aber es kommen dann unzählige Fehler.
>
> Beheben!

Wie anders beheben?
in der Header-datei:
1
extern volatile int Chip_Timeout;

[c}
Chip_Timeout = 0;
[/c]

dann kommt:
Error  80  undefined reference to `Chip_Timeout'  E:\....

: Bearbeitet durch User
von Harry L. (mysth)


Lesenswert?

foobar.h:
1
  extern volatile int chip_timeout;

foobar.c:
1
  volatile int chip_timeout;
2
3
void main()
4
{
5
  chip_timeout = 0;
6
}

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> in der Header-datei:
> extern volatile int Chip_Timeout; // Deklaration
Richtig!

Und in irgendeiner C Datei:
1
volatile int  Chip_Timeout = 0; // Definition und Initialisierung

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Arduino F. schrieb:
> Richtig!
>
> Und in irgendeiner C Datei:

Kann mir das bitte jemand näher erklären, warum es so funktioniert?

Ich dachte, es wäre dann eine doppelte Deklaration, wo der Compiler 
meckert.

von Harry L. (mysth)


Lesenswert?

in der Header-Datei:
extern volatile int  Chip_Timeout;

Das erklärt dem Compiler um welchen Datentyp es sich bei der Variable 
handelt.

Und in irgendeiner C Datei:
volatile int  Chip_Timeout = 0; // Definition und Initialisierung

Hier wird die Variable tatsächlich erzeugt und der Speicher dafür 
reserviert.
Das darf natürlich nur genau 1 mal passieren.

Der Linker sorgt dann dafür, das alle Referenzierungen der Variable auf 
den selben Speicher verweisen.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> Ich dachte, es wäre dann eine doppelte Deklaration,

Du darfst und kannst so oft deklarieren wie du willst.
Auch gerne 5000 mal nacheinander.
Aber Definitionen, die darf es nur einmal geben.

Tipp:
Du brauchst ein C Grundlagenbuch
Oder sofort C++

: Bearbeitet durch User
von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Ich denke das habe ich hier raus:
Link:Beitrag "Re: Globale Variablen, mehrere Dateien"

Da ist es so beschrieben, wie ich es bislang hatte.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Thomas S. schrieb:
> Da ist es so beschrieben,

Was ohne Grundlagenwissen einfach nur Schwachfug ist.
Nochmal, nimm dir ein C Buch zu Brust, oder besser sofort C++.
Aus Foren lernt man so nichts, oder oft auch das falsche.
Wie wie hier mal wieder gesehen haben.
Und das nicht zum ersten mal.

von Harry L. (mysth)


Lesenswert?


von N. M. (mani)


Lesenswert?

Thomas S. schrieb:
> Da ist es so beschrieben, wie ich es bislang hatte.

Da steht aber auch noch folgende Zeile:
1
#ifndef EXTERN
2
  #define EXTERN extern
3
#endif

Bin lieber ein vernünftiges Buch. Gibt es auch kostenlose im Internet.

von Rainer W. (rawi)


Lesenswert?

N. M. schrieb:
> Bin lieber ein vernünftiges Buch.

Du sprichst in Rätseln.

> Gibt es auch kostenlose im Internet.
Was kannst du empfehlen?

: Bearbeitet durch User
von N. M. (mani)


Lesenswert?

Rainer W. schrieb:
> N. M. schrieb:
>> Bin lieber ein vernünftiges Buch.
>
> Du sprichst in Rätseln.

Ja, Autovervollständigung ohne gegenlesen. Bin==Lies.

Rainer W. schrieb:
>> Gibt es auch kostenlose im Internet.
>
> Was kannst du empfehlen?

Nö, schon lange keins mehr gelesen. Von dem her kann ich gerade keins 
persönlich empfehlen.
Aber man bekommt definitv Skripte von FHs/Unis zum Thema. Und Bücher gab 
es auch schon immer kostenlose.

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


Lesenswert?

Thomas S. schrieb:
> Aber der Compiler meckert nicht, und es funktioniert.

Offenbar ja nicht, sonst hättest du den Thread nicht begonnen.

Daher bat ich eingangs um ein nachvollziehbares Beispiel. Dieses sollte 
auf das Minimum zusammen gestrichen sein, mit dem man sieht, wie du das 
aufgebaut hast.  (Ganz am Rande: wenn man sich die Arbeit macht, so ein 
Beispiel aus dem zusammen zu streichen, was man hat, versteht man häufig 
auch gleich sein Problem.)

von Rolf (rolf22)


Lesenswert?

Jörg W. schrieb:
> wenn man sich die Arbeit macht, so ein
> Beispiel aus dem zusammen zu streichen, was man hat, versteht man häufig
> auch gleich sein Problem

Sein Problem ist, dass er den Unterschied zwischen Deklaration und 
Definition nicht kennt und deshalb nicht berücksichtigt.

Für mich war das seinerzeit beim Lernen anhand von K & R zunächst 
verwirrend, weil beide Begriffe in der Normalsprache ja ungefähr 
dasselbe meinen.
Wenn ich definiere, was ein "Hund" ist, dann entsteht dadurch kein 
konkreter Hund. Ich sage damit lediglich,  wie ein Hund aussähe, wenn 
einer da wäre.
Nicht anders ist es, wenn ich vereinbare (deklariere), dass ich und 
andere ein Wesen mit bestimmten Eigenschaften (wenn denn ein solches 
Wesen da wäre) "Hund" nennen wollen. Wo ist da ein Unterschied?

In C entsteht durch eine Definition aber etwas durchaus Greifbares, 
nämlich ein reservierter Speicherplatz der definierten Größe im fertigen 
Programm; bei einer Deklaration entsteht gar nichts.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf schrieb:
> Sein Problem ist, dass er den Unterschied zwischen Deklaration und
> Definition nicht kennt und deshalb nicht berücksichtigt.

Zumal eine Definition ja in der Regel auch zugleich eine Deklaration 
ist. ;-)

Sollte an sich trotzdem nicht zu dem beschriebenen Effekt führen: wenn 
sowohl Deklaration als auch Definition das "volatile" haben, dann müsste 
es funktionieren – und den Unterschied zwischen beiden hat er am Ende 
mit seinem Headerfile-Trick mit EXTERN unbewusst tatsächlich realisiert.

von Thomas F. (igel)


Lesenswert?

Thomas S. schrieb:
> Erst in der Main, dann verschoben in der Header mit dem Zusatz 'EXTERN'

Hier eine gute Erklärung:

https://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files/1433387

Damit habe ich es auch hinbekommen...

von (prx) A. K. (prx)


Lesenswert?

Rolf schrieb:
> Wo ist da ein Unterschied?

Und dann ist da noch die Variable ohne Initialisierung, die ein Zwitter 
aus Definition und Deklaration sein kann.

PS: Im verlinkten Text unter "Not so good way" aufgeführt.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Es geht auch ohne extern!
(zumindest in C++, in C auch?)


In der Header Datei:
1
inline volatile int Chip_Timeout;
Danach kann man Chip_Timeout in jeder C++ (oder C?) Datei nutzen, in 
welcher die betreffende *.h Datei eingebunden ist.

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


Lesenswert?

Ist doch aber noch mehr 'ne Krücke, als sauber verstanden zu haben, wo 
man eine extern-Deklaration und wo man die Definition hinpackt.

Prinzipiell gab es bei C auch noch die Möglichkeit, dass sich mehrere 
gleichnamige Definitionen einander überlagern (im Stile eines FORTRAN 
COMMON-Blocks), und das war lange Zeit sogar der Default. Ich bin mir 
nicht ganz sicher, aber ich glaube mich zu erinnern, dass das in C23 
dann rausgeflogen oder zumindest deprecated ist.

Schlampig war es auf jeden Fall schon immer. ;-)

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jörg W. schrieb:
> Ist doch aber noch mehr 'ne Krücke,
Das ist in C++17 extra zu dem Zweck hinzugefügt worden.

Ist also weder historisch, noch schlampig.

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


Lesenswert?

Arduino F. schrieb:
> Ist also weder historisch, noch schlampig.

Nur schräg. ;-)

Ich bin mir nicht sicher, ob C sowas übernommen hat.

von Michael D. (nospam2000)


Lesenswert?

Thomas S. schrieb:

> Da ist es so beschrieben, wie ich es bislang hatte.

https://en.wikipedia.org/wiki/Cargo_cult_programming

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jörg W. schrieb:
> Nur schräg. ;-)
Einfacher!

Jörg W. schrieb:
> Ist doch aber noch mehr 'ne Krücke, als sauber verstanden zu haben, wo
> man eine extern-Deklaration und wo man die Definition hinpackt.

Ja, das sollte man verstanden haben, wenn man mit "historischem" C Code 
zu tun hat.
Aber irgendwann ist doch auch mal gut mit den alten Zöpfen.

von Harald K. (kirnbichler)


Lesenswert?

Arduino F. schrieb:
> Aber irgendwann ist doch auch mal gut mit den alten Zöpfen.

Warum? Was ist daran jetzt "besser"? Ist es nur besser, weil es neuer 
ist? Dann solltest Du Dich dafür einsetzen, daß Arduino nach Rust 
umzieht. Weil das ja neuer, ergo besser ist.

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


Lesenswert?

Arduino F. schrieb:
> Aber irgendwann ist doch auch mal gut mit den alten Zöpfen.

Dann sollte man wohl Rust nehmen.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Harald K. schrieb:
> Was ist daran jetzt "besser"?
Es ist an einem Ort zusammen gefasst!
Deklaration, Definition und Initialisierung.
Das macht Änderungen einfacher.

Man kann auf die *.cpp Dateien verzichten, wenn man denn möchte.

Für die Methoden einer Klasse braucht es auch keine *.cpp Datei mehr.
Auch wieder: Alles an einem Ort.

*.h only
Angeblich hilft das gar der Optimierung auf die Sprünge.
Wenn es auch das kompilieren verlängern mag.

Für mich macht es das "besser"!
Du magst anderer Ansicht sein.

Harald K. schrieb:
> weil es neuer ist?
Man muss nicht alles ablehnen nur weil neuer ist.
Oder?

Harald K. schrieb:
> Dann solltest Du Dich dafür einsetzen, daß Arduino nach Rust
> umzieht. Weil das ja neuer, ergo besser ist.
Mein AVR Arduino läuft mit C++23 und einer recht vollständigen 
libstdc++.
Neuer/Frischer geht es kaum, möchte ich mal sagen.

von Bruno V. (bruno_v)


Lesenswert?

Arduino F. schrieb:
> Für mich macht es das "besser"!
> Du magst anderer Ansicht sein.

Ich würde sagen: "Einfacher" und "Selbstkorrigierend". Wenn man 
vergisst, die Variable anzulegen, dann passiert das einfach automatisch 
"irgendwo" im Hintergrund. Allerdings auch bei Schreibfehlern. Oder wenn 
der Linker eigentlich ein Problem melden sollte.

Für mich ist das ähnlich dem Wunsch, dass "der Compiler doch die 
schließende Klammer hinzudenken kann, wenn klar ist, dass sie fehlt".

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Bruno V. schrieb:
> Wenn man
> vergisst, die Variable anzulegen, dann passiert das einfach automatisch
> "irgendwo" im Hintergrund. Allerdings auch bei Schreibfehlern. Oder wenn
> der Linker eigentlich ein Problem melden sollte.

Du hast eine witzige Fantasie, aber so funktioniert das nicht!

Dadurch, dass die inline Variable in der *.h definiert wird, gibt es 
diese in jeder Übersetzungseinheit, welche diese *.h einbindet.
Durch das inline Attribut wird dem Linker mitgeteilt, dass er sie alle 
zusammen legen soll, auf ein einziges Speicherobjekt.
Das gilt übrigens ganz ähnlich auch für inline Funktionen.
Methoden sind übrigens per default inline, wenn sie im Klassenkörper 
definiert werden. Und das ist schon viel länger so.
Das Prinzip wurde nur auf freie Funktionen und Variablen ausgedehnt, so 
dass es für den Programmierer gezielt nutzbar wird.

Da ist nix mit "automatisch" Klammern basteln, Schreibfehler korrigieren 
oder so....

Bruno V. schrieb:
> "Selbstkorrigierend".
Nix davon ist "Selbstkorrigierend".
Sondern das ist Teil der Sprachdefinition ab C++17
Das muss dir ja nicht schmecken, aber es ist jetzt so.
Auch kannst du weiterhin dein extern nach belieben weiter nutzen.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Oder sofort C++

Arduino F. schrieb:
> oder besser sofort C++.

Arduino F. schrieb:
> zumindest in C++

Arduino F. schrieb:
> Danach kann man Chip_Timeout in jeder C++

Arduino F. schrieb:
> Das ist in C++17

Arduino F. schrieb:
> Mein AVR Arduino läuft mit C++23

Sag mal, welches Problem hast du eigentlich?

Die Frage bezieht sich ganz klar auf C.

Und das einzige was du kannst ist alles mit C++ zuzuspammen.

Und was "dein Arduino" macht interessiert keinen.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Auch in C geht das durch, allerdings mit einer Warnung:
warning: variable 'testVar' declared 'inline'

Ohne inline, ein Linker Error:  multiple definition of `testVar';

Getestet mit -std=gnu11 und -std=gnu23

Und ja alle diese inline Variablen, mit gleichem Namen,  werden auf die 
gleiche Adresse gelinkt.



Johann L. schrieb:
> Die Frage bezieht sich ganz klar auf C.
Geht also genau so mit gnu C!

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


Lesenswert?

Arduino F. schrieb:
> Und ja alle diese inline Variablen, mit gleichem Namen,  werden auf die
> gleiche Adresse gelinkt.

Das war bis vor wenigen Jahren in C der Standard bei vielen Compilern 
(common block). Kannst du wahrscheinlich mit -fcommon immer noch 
reaktivieren.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Harry L. schrieb:
> foobar.h:
>
>   extern volatile int chip_timeout;
>
> foobar.c:
>
>   volatile int chip_timeout;
>
> void main()
> {
>   chip_timeout = 0;
> }

Das ist schlecht. Besser wäre für foobar.c:

foobar.c:
1
#include "foobar.h"  // WICHTIG!
2
volatile int chip_timeout;
3
4
void main()
5
{
6
  chip_timeout = 0;
7
}

Nur so kann der Compiler prüfen, ob Deklaration und Definition vom 
selben Typ sind. Das ist extrem wichtig. Sonst könnte man in foobar.c 
die Variable auch abweichend als float definieren und keiner würde das 
merken!

Fazit:

In der Datei, wo die globale Variable definiert wird, sollte auch die 
externe Deklaration (in Form des include-Files) immer mit herangezogen 
werden, damit der Compiler prüfen kann, ob beide "kompatibel" sind.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jörg W. schrieb:
> Kannst du wahrscheinlich mit -fcommon immer noch
> reaktivieren.

Nein, der Schalter hat weder in -std=gnu11 noch in -std=gnu23 irgendeine 
erkennbare Wirkung.

von Bruno V. (bruno_v)


Lesenswert?

Arduino F. schrieb:

> Du hast eine witzige Fantasie, aber so funktioniert das nicht!
In C schon.

> Das Prinzip wurde nur auf freie Funktionen und Variablen ausgedehnt, so
> dass es für den Programmierer gezielt nutzbar wird.
Die ursprüngliche Bedeutung von inline war, den Aufruf möglichst schnell 
zu machen.

Dass es zur Definition im Header verwendet wird, ist eher eine 
Bedeutungsverschiebung. In C++ auch einleuchtend, da der C++-Compiler 
sich eh schon immer die Freiheit zum inline herausgenommen hat.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Bruno V. schrieb:
> In C schon.
Nein, auch C fügt keine Klammern hinzu oder korrigiert sonst 
fehlerhaften Code.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Und ja alle diese inline Variablen, mit gleichem Namen,  werden auf die
> gleiche Adresse gelinkt.

Nein, werden sie nicht.

Es gibt schlicht und einfach ein "multiple definition of" Fehler vom 
Linker, es sei denn du compilierst mit -fcommon.

Ws wird einfach nur das "inline" vom Compiler ignoriert, das ist alles. 
Zumindest wenn man ohne -Werror übersetzt.

Und C++ inline variables werden auf Target-Ebene komplett anders 
umgesetzt (comdat oder linkonce) als inline Funktionen.

Einfach alles Käse was du hier verzapfst.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Nein, werden sie nicht.

Du hast recht!
Mein Test war fehlerhaft.
Habe ich gerade auch bemerkt!

Dank eines dummen Tippfehlers ist aus meiner beabsichtigten a.c Datei 
eine ac.ino geworden.
Ist mir gerade erst aufgefallen.

Nach der Korrektur zeigt sich, dass C das nicht so tut.
Also ab jetzt behaupte ich das Gegenteil von eben.

: Bearbeitet durch User
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.