Forum: Compiler & IDEs Variablen Gültigkeitsbereich in C: Definition in Bibliothek, Zugriff aus Hauptprogramm


von Ralph K. (rosti_mcn)


Lesenswert?

Hallo zusammen,

ich bin (außer als Leser) neu hier und wie es ausschaut immer noch 
Anfänger - wenn auch nicht mehr ganz so blutüberschmiert :D

Ich übe gerade den Umgang mit ATmegas am Beispiel der 
Luftfeuchtigkeitssensoren DHT11/22, und mein Code macht mich noch 
wahnsinnig. Hab schon eine Nacht hinter mir ;) Deswegen möchte ich gern 
eine Frage zu einem der Probleme loswerden:

Ausgangslage:

* der Code, der den Sensor auslesen soll, ist in einer Lib
* die binde ich vom separaten Hauptprogramm dann ein und linke das Ganze 
zusammen
* zum Debuggen möchte ich Arrays mit Statusinformationen innerhalb einer 
Bibliotheksfunktion schreiben, die Arrays sind in der Bibliothek 
definiert

Das Problem:

Aus blanker Naivität wollte ich nun vom Hauptprogramm aus direkt auf die 
Arrays zugreifen. Also:

extern uint8_t * array1;  // usw.

deklariert und los gehts. Allerdings geht es schief. Lesezugriffe 
ergeben irgendwas und ein testweiser Schreibzugriff führt ins Nirvana.
Was genau passiert hier? Wie kann man das korrekt lösen? (Außer halt die 
Datenausgabe auch gleich in eine Lib-Funktion zu packen - ich will 
LERNEN!)

Übrigens: mir ist schon klar, dass da diverse Bibliotheken für die 
DHT11/22 existieren. Aber wie gesagt, dass ganze ist für mich auch und 
nicht zuletzt eine Übung. Daher: Ausweichen auf eine existierende Lib 
kommt nicht in Frage :D

Grüße von der zugefrorenen Havel
und schon mal vielen Dank an alle Helfer :)

Ralph

von Stefan E. (sternst)


Lesenswert?

Ralph K. schrieb:
> Aus blanker Naivität wollte ich nun vom Hauptprogramm aus direkt auf die
> Arrays zugreifen. Also:
>
> extern uint8_t * array1;  // usw.

1
extern uint8_t array1[];

von Ralph K. (rosti_mcn)


Lesenswert?

Stefan Ernst schrieb:
> extern uint8_t array1[];

Danke. Aber warum??? Würde das gern im Detail verstehen.

von Stefan E. (sternst)


Lesenswert?

Ralph K. schrieb:
> Aber warum??? Würde das gern im Detail verstehen.

1
extern uint8_t * array1;
Was sagst du denn dem Compiler damit?
Du sagst, dass an der Adresse "array1" ein Pointer liegt, der auf ein 
uint8_t zeigt. Wenn als Array interpretiert (array1[x]), ist es halt ein 
Pointer auf das erste Element. Und, spiegelt das deine Situation wieder? 
Nein, denn du hast an der Adresse "array1" ja keinen Pointer auf das 
Array, sondern das Array selber.

Entgegen dem, was manche Möchtegern-Experten gerne predigen, ist ein 
Array nun mal eben nicht immer äquivalent zu einen Pointer auf das erste 
Element.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Stefan hat schon alles Wesentliche gesagt. Hier sind noch ein paar
zusätzliche Hinweise:

Generell sollte eine Extern-Deklaration genauso aussehen wie die
ursprüngliche Variablendefinition, nur eben mit dem Zusatz "extern".
Wenn in der Bibliothek also steht:
1
uint8_t array1[9];

dann sollte in den anderen Modulen, die diese Variable verwenden,
1
extern uint8_t array1[9];

stehen.

Jetzt ist es aber so, dass der Compiler die Arraygröße nur in der
Variablendefinition braucht, nämlich um eine passendes Stück Speicher
dafür zu reservieren. Bei bloßen Zugriffen auf das Array ist diese
Information uninteressant, da in C keine Überprüfung der Array-Grenzen
erfolgt. Also kann man die Größe genauso gut weglassen und schreibt
einfach
1
extern uint8_t array1[];



Es ist aber falsch, zu schreiben
1
extern uint8_t *array1;

denn ein Pointer ist nicht das Gleiche wie ein Array, auch wenn in
vielen Fällen (z.B. bei den Funktionsargumenten) ein Array durch einen
Pointer auf sein erstes Element ersetzt wird.

In diesem Fall würde der Compiler annehmen, dass in der Bibliothek
tatsächlich ein Pointer definiert wurde:
1
uint8_t *array1;

Wenn du in deinem Hauptprogramm bspw.
1
x = array1[4];

schreibst, ist das das Gleiche wie
1
x = *(array1 + 4);

Da array1 fälschlicherweise als Pointer angesehen wird, wird
nachgeschaut, was der Inhalt dieser Pointer-Variable ist, d.h. der
Array-Inhalt wird als Pointer interpretiert. Da sich ein Pointer auf dem
ATmega aus 2 Bytes zusammensetzt, werden also die ersten beiden Bytes
des in der Bibliothek definierten Arrays gelesen (array1[0] und
array1[1]). Aus diesen wird eine 16-Bit-Speicheradresse gebildet, der
Index 4 hinzuaddiert und von der Ergebnisadresse ein Datenbyte in die
Variable x gelesen.
1
x = array1[4];

hat somit den gleichen Effekt wie
1
x = *(uint8_t *)(array1[0] + 256 * array1[1]);

Das ist aber garantiert nicht das, was du möchtest.



Um solche Fehler zu vermeiden, ist es gängige Praxis, dass alle
Extern-Deklarationen in ein Header-File geschrieben werden, und dieses
Header-File nicht dort includet wird, wo die Variable verwendet wird,
sondern auch dort, wo sie definiert wird. Stimmt nun der Typ der
Extern-Deklarationen nicht mit dem der Variablendefinition überein,
liefert der Compiler eine Fehlermeldung.

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.