Forum: Compiler & IDEs Anfängerfrage: Wie deklariere ich meine Variable richtig?


von Andreas (Gast)


Lesenswert?

Hallo,

ich bin absoluter Neuling in Sachen C-Programmierung. Ich bin bereit
auch ein bisschen dafür zu arbeiten und lese in Büchern, stöbere in
allen Foren herum und lasse nichts aus, was Infos bringt. Nun hab ich
mich an mein erstes Projekt herangetraut und komm nicht richtig weiter.
Als Prozessor habe ich den ATMega8.
Mein Problem: Ich möchte innerhalb des Programmes eine Variable
festlegen, die für mehrere Funktionen erreichbar sein soll. Ändert eine
der Funktionen diese Variable, müssen die anderen auch später noch
darauf zugreifen können. ich  schreibe also "uint8_t Port;", nachdem
ich ausserhalb dieses Files nicht darauf zugreife, brauche ich doch
wohl keine extern. Die Deklaration steht am Anfang des files und nicht
in irgendeiner Funktion. Nun übergebe ich die variable an eine Funktion
"static void send_Port (Port);"dort wird alles abgearbeitet und kehrt
danach zurück. Wenn ich nun mit "Port" wweiterarbeiten will, nimmt er
statt dessen wieder sein normales Arbeitsregister, in dem irgendwas
steht. Kann mir da einer eine kurze Erklärung abgeben? Wäre mir sehr
geholfen damit.

Andreas

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


Lesenswert?

Ein (minimales) Codebeispiel wäre hilfreich.  Es gibt ein paar
Dinge, die hier zuschlagen können.  Ein IO-Port lässt sich nicht
ohne weiteres als Variable benutzen, und die Kommunikation
zwischen Interruptroutinen und einem Hauptprogramm braucht auch
zusätzliche Vorkehrungen (siehe avr-libc FAQ, Punkt 1).

von Rolf Magnus (Gast)


Lesenswert?

> ich bin absoluter Neuling in Sachen C-Programmierung. Ich bin
> bereit auch ein bisschen dafür zu arbeiten

Gut, denn sonst könntest du's gleich vergessen. C muß man schon
vernünftig lernen. C ist zware eine einfache Sprache, aber ihre
korrekte Anwendung ist ganz und gar nicht einfach.

> und lese in Büchern, stöbere in allen Foren herum und lasse nichts
> aus, was Infos bringt. Nun hab ich mich an mein erstes Projekt
> herangetraut und komm nicht richtig weiter.

Das geht wohl den meisten anfangs so. Ist ganz normal.

> Als Prozessor habe ich den ATMega8.
> Mein Problem: Ich möchte innerhalb des Programmes eine Variable
> festlegen, die für mehrere Funktionen erreichbar sein soll. Ändert
> eine der Funktionen diese Variable, müssen die anderen auch später
> noch darauf zugreifen können. ich  schreibe also "uint8_t Port;",
> nachdem ich ausserhalb dieses Files nicht darauf zugreife, brauche
> ich doch wohl keine extern.

Sie ist per default extern. Um das abzuschalten, mußt du noch ein
"static" davorhängen. Wenn du ein "extern" davorschreiben würdest,
würde das den Namen bekanntmachen, aber nicht den Speicher dafür
reservieren.

> Die Deklaration

Beachte den Unterschied zwischen Deklaration und Definition. Eine
Definition ist zwar immer auch eine Deklaration, aber umgekehrt gilt
das nicht. Eine Deklaration einer Variablen macht nur den Namen und Typ
bekannt, aber reserviert keinen Speicher. Das macht die Definition.

> steht am Anfang des files und nicht in irgendeiner Funktion.

Also eine globale Variable.

> Nun übergebe ich die variable an eine Funktion "static void
> send_Port (Port);"dort wird alles abgearbeitet und kehrt
> danach zurück. Wenn ich nun mit "Port" wweiterarbeiten will, nimmt
> er statt dessen wieder sein normales Arbeitsregister, in dem
> irgendwas steht. Kann mir da einer eine kurze Erklärung abgeben?

Die globale Variable Port wird "by value" an die Funktion übergeben.
Das heißt, daß ihr Wert in das Funktionsargument kopiert wird. Mit
anderen Worten: Die Funktion benutzt - und ändert - nur eine lokale
Kopie, die beim Verlassen der Funktion aufhört zu existieren. Deshalb
ändert sich an der globalen Variable nichts.
Eine Lösung wäre, den Port als Rückgabewert aus der Funktion zu holen,
also diese so definieren:

  uint8_t send_Port();

und dann:

  Port = send_Port();

Falls die Funktion den alten Wert braucht, geht natürlich auch:

  uint8_t send_Port(uint8_t oldPort);

und dann:

  Port = send_Port(Port);

Alternativ kannst du auch einen Zeiger als Parameter nehmen. Damit wird
nicht der Wert, sondern die Adresse an die Funktion übergeben. Dadurch
kann sie die Variable selbst ändern. Das ginge etwa so:

  void send_Port(uint8_t* Port)
  {
      *Port = neuer_wert;
      /*...*/
  }

und dann:

  send_Port(&Port);

von Andreas (Gast)


Lesenswert?

Danke Rolf,
aller Anfang ist schwer. Ich denke mal, ich bin nicht der erste, der C
im Alleingang angeht. Anfangs holperts noch, aber wenn man seine Codes
mit denen von anderen vergleicht, fällt doch irgendwann mal auf, was
man weniger elegant als die anderen macht. Ich bleib dran.
Deklaration und Definition ist bei mir noch nicht ins Blut
übergegangen. Dank dir nochmal für die richtige Erläuterung. Die Sache
mit dem Zeiger hab ich noch nicht angegangen. Wenn mein Programm mal
laufen sollte, kann ich mir fürs nächste mal überlegen das spaßeshalber
einzubauen.
An die Möglichkeit, den Wert wieder zurückzugeben, hab ich auch schon
gedacht. Vermutlich wirds darauf hinaus laufen. Ich hab wieder was
gelernt und danke dir.

Andreas

** Jörg

Tut mir leid, wenn ich den Namen "Port" etwas dämlich gewählt habe.
Ich will damit nicht auf einen IO-Port zugreifen, sondern meine
verwendete Variable heißt so ähnlich.

von Christian Axtmann (Gast)


Lesenswert?

Kann man denn die Varialbe nicht "by reference" übergeben, oder ändert
er dann die originale Variable noch immer nicht?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

.

  "Kann man denn die Varialbe nicht "by reference" übergeben,
  oder ändert er dann die originale Variable noch immer nicht?"

Genau das geschieht, wenn ein Pointer auf die Variable übergeben wird,
wie Rolf am Ende seines Postings bereits schilderte:

  "Alternativ kannst du auch einen Zeiger als Parameter
  nehmen. Damit wird nicht der Wert, sondern die Adresse
  an die Funktion übergeben. Dadurch kann sie die Variable
  selbst ändern. Das ginge etwa so:

    void send_Port(uint8_t* Port)
    {
        *Port = neuer_wert;
        /*...*/
    }

  und dann:

    send_Port(&Port);

  (Zitat Rolf Ende)

In C++ gibt es dafür noch das Konstrukt der "Referenz", das einem
echten Pointer gegenüber den Vorteil bietet, daß keine
Nullpointerzugriffe auftreten können.

  void send_Port(uint8_t &Port)
  {
    Port = neuer_wert;
    /* ... */
  }

IMHO ein Nachteil ist allerdings, daß beim Aufruf einer solchen
Funktion kein Unterschied zum "call by value" zu erkennen ist:

  send_Port(Wert);

Ohne sich den Prototypen der Funktion anzusehen, weiß man also nicht,
ob da ein "call by value" oder ein "call by reference" geschieht.
Das ist IMHO kein Fortschritt.

Allerdings ist das Konstrukt der Referenz ein für das Überladen von
Operatoren zwingend erforderliches Sprachkonstrukt, daher muss man
damit leben.

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


Lesenswert?

> In C++ gibt es dafür noch das Konstrukt der "Referenz", das einem
> echten Pointer gegenüber den Vorteil bietet, daß keine
> Nullpointerzugriffe auftreten können.

Wenn man die Adresse eines existierenden Objekts als Zeiger übergibt,
gibt's auch in C keine Nullpointer-Zugriffe. ;-)  Dieses Problem hat
man erst mit dynamic memory allocation (und dort auch nur, wenn man
sie nicht anderweitig ordentlich abfängt), aber dagegen würde auch
kein Referenzparameter helfen...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Du hast natürlich -wie so oft- Recht; C erfordert halt eine gewisse
Disziplin, die möglicherweise dem einen oder anderen Anfänger
schwerfällt. Wenn man ausreichend lange/viel in C programmiert hat,
dann sind das alles Dinge, die so selbstverständlich sind wie das Atmen
...

C ist eine wundervolle Programmiersprache, sofern man nicht das Pech
hat, mit echtem "K&R"-C (vor C89) konfrontiert zu werden.

von Andreas (Gast)


Lesenswert?

Hallo,
ich seh schon! Ich hab noch einiges zu lernen. Die letzten Beiträge
übersteigen momentan noch mein Wissen. Ich habe aber einen Teilerfolg
errungen. Mit "volatile" habe ich keine Probleme mehr mit dem
überschreiben. Wenn ich mich nicht irre, schreibt er die Variable dann
wohl in den Speicher und nicht in ein Register. Ist das so? Schlagt
mich nicht, wenn ich hier Blödsinn verkünde. Leider hatte ich noch
keine Zeit, das Phänomen im Buch nachzuschlagen.

Andreas

von Rolf Magnus (Gast)


Lesenswert?

>  Wenn ich mich nicht irre, schreibt er die Variable dann
> wohl in den Speicher und nicht in ein Register. Ist das so?

Mehr oder weniger. Der Compiler geht im Normalfall davon aus, daß die
Variable nur durch das Programm geändert werden kann. Und "Programm"
ist hier nur der normale Programmfluss, denn der Compiler weiß ja
nichts von Interrupts und davon, wann die zuschlagen können. Daher kann
er bei Berechnungne, wenn genug Register übrig sind, sich den Wert in
einem Register merken, statt jedesmal in den Speicher zu schreiben und
wieder auszulesen. Unter Umständen kann der Compiler sogar den Speicher
weglassen und die Variable nur im Register halten. Das volatile sagt
nun, dem Compiler, daß die Variable sich jederzeit unerwartet ändern
kann und er die genannten Optimierungen nicht durchführen darf.

von Werner B. (Gast)


Lesenswert?

Analogie aus dem täglichen Leben!?

call by value:
Du bekommst ein Fotokopie des Orginals und kannst damit machen was Du
willt, das juckt das Orginal nicht die Bohne.

call by reference:
Du bekommst einen Zettel auf dem steht wo sich das Orginal befindet.
Wenn Du wissen willst was dort steht musst Du selbst hingehen und
nachsehen. Du kannst somit auch auf dem Orginal rumkrizeln und es
verändern. Danach können auch alle anderen die wissen wo sich das
Orginal befindet Deine Änderungen sehen.
Hier kommt jetzt noch "volatile" ins Spiel. Ist die Variable (das
Orginal) nicht volatile, darf sich jeder eine eigene Kopie machen und
vertraut darauf dass sich diese solange nicht verändert wie er diese
benötigt; Ist die Varaiable volatile deklariert, bedeutet dies
explizit, dass deine eigene Kopie warscheinlich nicht aktuell ist, da
noch jemand (im Hintergrund) jederzeit am Orginal herumdrehen darf. Du
(der Prozesor) musst also jedesmal nachsehen was dort steht bevor Du
diese Information verwendest.

von Horst-Detlef (Gast)


Lesenswert?

Ich schenk' Dir mal einige Buchstaben:
iiiiiii

O-R-I-G-I-N-A-L, verdammich!

Mir wäre das ja peinlich, fortwährend ein so einfaches Wort hartnäckig
falsch zu schreiben, aber vielleicht ist das ja cooler Stil.

von Werner B. (Gast)


Lesenswert?

Der Begriff des Orginals, vom lateinischen Substaniv "origo"
abstammend, bedeutet soviel wie Urfassung, Urbild, Stammvater.
Unser Fremdwort "Orginal" übersetzt der Duden mit "eigentümlich,
durch eine besondere Art auffallender Mensch, Sonderling".

icur2cool4me  ;-)

von Horst-Detlef (Gast)


Lesenswert?

Das ist ja nicht zu fassen!

Es heisst "Or_i_ginal" und nicht "Orginal".

Auch im Duden steht

1. ori|gi|nal  <Adj.> [lat. originalis = ursprünglich, zu: origo (Gen.:
originis) = Ursprung, Quelle, Stamm, zu: oriri, ...

"Orginal" ist dem Duden hingegen unbekannt.

von ---- (Gast)


Lesenswert?

> aber vielleicht ist das ja cooler Stil.
Nein, das ist nur ein ganz normaler Schreibfehler, den Werner jetzt
bestimmt nicht mehr machen wird. Ich finde den Hinweis im Prinzip
korrekt, aber den Ton etwas übertrieben.

----, (QuadDash).

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.