Forum: Compiler & IDEs Pointer Gültigkeitsbereich


von Helmut (Gast)


Lesenswert?

Hallo an alle!

Ich habe anscheinend ein grundlegendes Verständnisproblem mit dem
Gültikeitsbereich mit Pointern, da mich das gestern Stunden an Zeit
kostete, einen Fehler in einem kleinen Programm zu finden und auch das
nur durch Zufall.

Also:

Ein Pointer hat so wie ich das verstanden habe den selbe
Gültikeitsbereich wie jede andere variable, unabhängig vom Datentyp.

Wenn ich also zu Beginn des Programmes (vor allen Funktionen und
Klammern schreibe:

volatile uint8_t *port;

Dann ist der Pointer "port" ja global deklariert, richtig?

SO und nun folgendes Beispiel:

In meinem weiteren Programm gibt es die Funktionen:

int debounce_pin(volatile uint8_t *port, uint8_t pin)
ISR(TIMER0_OVF_vect)   und
int main(void)

Nun hatte ich den Fehler, dass ich der meinung war, wenn der Pointer
"port" ganzoben global deklariert ist, dass der in allen Funktionen
des Programmes bekannt sei....war er aber nicht!

Nun frag ich euch, warum nicht!
Ich kam dahinter, dass mir die Zuweisung des Pointers fehlte, was nun
so aus sieht und auch funktioniert:

port = &PINC   //Dem pointer die Adresse von PINC übergeben

Schön und gut, ABER: Ich war immer der Meinung, dass variablen immer
nur innerhalb der Klammern, in denen Sie stehen, gültig sind.(außer
globale und "port" ist doch global?!)

Wenn ich nun diese Pointerzuweisung in in die Funktion, in der "port"
dereferenziert wird schreibe,funktioniert das ganze Programm, wenn ich
es allerdings ins main Programm schreibe, funktioniert es AUCH!!

Nun dachte ich mir, dass liegt daran, das "port" ja gobal definiert
ist.
Sschreibe ich aber die Zuweisung in eine andere Funktion, geht gar
nichts mehr.

Die Frage, die sich mir also stellt:
Ist es richtig, dass der Gültigkeitsbereich nicht von der Deklaration
der variable anhängt (wo die steht), sondern wo im Programm die
Zuweisung passiert??

Bin für jede Hilfe dankbar !!

Grüße Helmut

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Du benutzt zwei Pointer mit dem gleichen Namen, einer davon wird beim
Eintritt in 'debounce_pin' auf dem Stack angelegt, eine liegt im
Bereich der statischen, globalen Variablen. Wenn 'debounce_pin'
betreten wird, gilt innerhalb dieser Funktion nur die Referenz auf den
Stack. Wird sie verlassen, wird natürlich auch dieser Pointer ungültig
und damit "Zerstört".

von Helmut (Gast)


Lesenswert?

P.S.:

Die Zuweisung funktioniert auch NICHT, wenn ich die direkt nach der
Deklaration der pointers ganz zu Beginn mache:


volatile uint8_t *port;
port = &PINC;

Warum nicht?

von Helmut (Gast)


Lesenswert?

Na gut aber was ich mich nun frage ist:

Wenn ich der Funktion:


int debounce_pin(volatile uint8_t *port, uint8_t pin)

so etwas übergebe:


debounce(&PINC,PINC0);

Funktioniert das, egal, wie ich den ersten parameter bei der Definition
der FUnktion debounce heißt, das funktioniet ja auch,wenn die Funktion
so aussieht:

int debounce_pin(volatile uint8_t *test, uint8_t pin)

Das versteh ich nicht so ganz. In diesem Falle wird beim Betreten der
Funktion debounce ein pointer "erstellt?", der den Namen "test"
hat. In diesem Falle ist ja egal, wie ich diesen pointer nenne,
richtig?

Der poiter "test" hat also nur gültigkeit innerhalb eines Aufrufes
von debounce?

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


Lesenswert?

> Warum nicht?

Weil du Patricks Antwort noch nicht verstanden hast.

Benenne deinen Funktionsparameter um von "port" auf "p" oder
sowas, dann hast du sowohl "port" (global) als auch den
Parameter (lokal) zur Verfügung innerhalb der Funktion.

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


Lesenswert?

> Der poiter "test" hat also nur gültigkeit innerhalb eines Aufrufes
> von debounce?

Ja, genauso wie "pin".  Alle Funktionsparameter erzeugen lokale
Variablen innerhalb der Funktion, ob sie nun Pointer sind oder
nicht, spielt dabei keine Rolle.

von Helmut (Gast)


Lesenswert?

"Benenne deinen Funktionsparameter um von "port" auf "p" oder
sowas,dann hast du sowohl "port" (global) als auch den
Parameter (lokal) zur Verfügung innerhalb der Funktion."

Nun, heißt das, dass der globale Pointer und der lokale nicht gleich
heißen dürfen, da sonst beim Eintritt in die Funktion "debounce"
plötzlich zwei Pointer mit dem selben Namen existieren?

von Rolf Magnus (Gast)


Lesenswert?

Sie dürfen schon gleich heißen. Allerdings kannst du dann aus der
Funktion heraus auf den globalen nicht zugreifen. Es wird immer der
lokale verwendet.

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


Lesenswert?

Dürfen darfst du schon.  Innerhalb der Funktion existiert aber nur der
lokale Name; der globale wird damit überschattet und ist dort nicht
zugreifbar.  Wie sollte auch der Compiler ahnen, welchen der beiden
Zeiger mit dem Namen "port" du gerade meinst?

Im Grunde genommen kannst du sogar doppelte Überschattung
haben:
1
void *port;
2
3
void foo(void *port)
4
{
5
   void *port;
6
   ...
7
}
In diesem Falle würde die lokale Variable namens port den Parameter
namens port überschatten (das generiert eine Compilerwarnung), der
Parameter wiederum überschattet die globale Variable namens port (das
generiert keine Compilerwarnung).

Aber wie geschrieben: mit Zeigern hat das nichts zu tun, das sind
ganz allgemeine Namensregeln für Variable.

von Helmut (Gast)


Lesenswert?

WO ich nicht durchblicke ist:

Wenn ich ganz zu Beginn schreibe(wie schon oben erwähnt) :

volatile uint8_t *port;
port = &PINC;

Dann habe ich einen globalen Pointer mit dem Namen "port", der die
Adresse von PINC enthält.

Mal abgesehen von der zusätzlichen lokalen Pointer-Variable in der
debounce Funktion(diese wurde ja noch gar nicht aufgerufen), sollte nun
in der ISR zumindest der globale Pointer richtig initialisiert sein.

Ich bekomme aber einen Fehler, wenn ich in der ISR , den pointer
dereferenzieren möchte mit:

neuevariable = *port & (1 << pin);

SO als ob er nicht bekannt wäre.....
Wie gesagt, dass alles hat ja noch gar nichts zu tun mit dem lokalen
Pointer in "debounce"?

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


Lesenswert?

Wenn du dir jetzt noch die Fehlermeldung nicht aus der Nase
ziehen ließest, könnte dir geholfen werden...  Am besten gleich
ein komplettes, compilierbares Stück Code als Attachment.

von Helmut (Gast)


Angehängte Dateien:

Lesenswert?

Ok, hier das gesamte Programm, kompilierbar mit avr-gcc (GCC) 3.4.5

Die Frage habe ich als Kommentar neben die betroffene Zeile geschrieben
;-)

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


Lesenswert?

Umm, oh, bitte keine Headerfiles aus der Bibliothek in dein Projekt
mit hineinlegen.  Wenn die Bibliothek mal aktualisert wird, benutzt du
dann (u. U.) noch die alten Dateien.

Damit man das Ganze auch ohne all die lokalen Kopien noch compilieren
kann, musst du aus

#include "interrupt.h"

ein

#include <avr/interrupt.h>

machen.
1
port = &PINC; //WARUM DARF DIESE ZEILE NICHT GANZ OBEN STEHEN UNTER
2
              //DER DEKLARATION DES GLOBALEN POINTERS?

Weil sie ausführbarer Code ist, der darf in C nur innerhalb von
Funktionen auftauchen.  Außerhalb von Funktionen dürfen nur
Deklarationen und Definitionen stehen, kein Code selbst.  Was
aber geht ist eine Initialisierung auf globaler Ebene:
1
volatile uint8_t *port = &PINC;

Das ist kein ausführbarer Code, sondern es beschreibt eine
Vorbelegung der Variablen "port" mit einem Wert.  Diese Vorbelegung
generiert der Compiler bereits zur Compilezeit, wobei er im Falle des
AVR dann zur Laufzeit während des Starts ein wenig Hilfe vom startup
code braucht, damit dieser die RAM-Zellen aus dem ROM initialiert.

von Helmut (Gast)


Lesenswert?

AAhhhh...

"Weil sie ausführbarer Code ist, der darf in C nur innerhalb von
Funktionen auftauchen.  Außerhalb von Funktionen dürfen nur
Deklarationen und Definitionen stehen, kein Code selbst."

Das sollte man halt wissen :-P

Danke für deine Mühe, Jörg!!

Ab jetzt werd ich wohl öfters diese "Initialisierung auf globaler
Ebene" vornehmen, erspart eine Menge Ärger :-)

von Helmut (Gast)


Lesenswert?

Achja, eines noch!

Wie das mit der globalen und der lokalen Deklaramtion und der
Namensgebung von Variablen funktioniert, hab ich nun glaub ich
verstanden.

Was mir noch einfällt, ist etwas bezüglich der Initialisierung.

Wenn der pointer nicht global initialisiert wird mit

volatile uint8_t *port = &PINC;

sondern eben nur global deklariert mit

volatile uint8_t *port;

Wie ist es in diesem Fall (allgemein) mit der Initialisierung?
Kann hier grundsätzlich in jeder beliebigen Unterfunktion diese
Initialisierung stattfinden(die Variable ist bis zu diesem Zeitpunkt
zwar deklariert, besitzt aber noch keinen korrekten Wert/Adresse) oder
muss das z.B.: im main.c sein?

Gruß Helmut

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


Lesenswert?

> Kann hier grundsätzlich in jeder beliebigen Unterfunktion diese
> Initialisierung stattfinden(die Variable ist bis zu diesem Zeitpunkt
> zwar deklariert, besitzt aber noch keinen korrekten Wert/Adresse)

Falsch, sie besitzt einen korrekten Wert: den Nullzeiger.  Das ist vom
C-Standard abgedeckt, d. h. du darfst dich drauf verlassen.

> oder muss das z.B.: im main.c sein?

Eine Zuweisung auf ein globales Objekt darf aus jeder beliebigen
Funktion heraus erfolgen.

von Helmut (Gast)


Angehängte Dateien:

Lesenswert?

Alles klar, ich habe nun einen dieser Pointer ersetzt durch zwei defines
und ein paar kleinen Änderungen im Code, da der eine ziemlich unnötig
war...

Ansonsten scheint das Programm so zu funktionieren, wenn auch nicht
immer....manchmal beginnen die LEDs am Port B zu blinken, obwohl Sie
zuvor schon geleuchtet hatten (was signalisieren sollte, dass der
taster stabil ist)...

Und das, obwohl ich die Interrupts zu diesem Zeitpunkt deaktiviere...

Wie auch immer, falls jemand Zeit und Lust hat, kann er ja einen kurzen
Blick auf meinen aktuellen Code werfen (im Anhang), vielleicht hab ich
noch wo einen Denkfehler...

Grüße, Helmut

von Walter (Gast)


Lesenswert?

dein main Programm ist nur zum einmaligen Gebrauch bestimmt:
du wartest bis der Taster stabil ist, dann setzt du den Port auf 0,
und dann??

Walter

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.