C für PICs: Variablen



zurück zu C für PICs , C-Compiler , PIC-Prozessoren , Elektronik , Homepage

C für PICs
Grundlagen von C
Variablen
Funktionen
Operationen
Programmablaufsteuerung
Arrays und Strings
Pointer
Strukturierte Typen
PIC-spezifisches


zurück zu C für PICs


Variablen

Eine Variable ist ein Stückchen Arbeitsspeicher des PIC, in dem man einen Wert (z.B. eine Zahl) speichern kann. Jede Variable bekommt vom Programmierer einen Namen und einen Typ ("Größe" des RAM-Bereichs). Mit Variablen kann man rechnen, und ihr Wert kann durch Operationen im Programm verändert werden, woher wohl auch der Name stammt.


Datentypen

Eine Speicherzelle des PIC ist 1 Byte (8 Bit) groß. Damit lässt sich eine Zahl mit einem Wert von 0 bis 255 speichern. Will man mit anderen Werten (größere Zahlen, negative Zahlen, Fließkommazahlen, Textzeichen) arbeiten, so muss der Zahlenwert im Byte anders interpretiert werden oder es müssen sogar mehrere Bytes zu einer Variablen zusammengefasst werden. Man bezeichnet das dann als Datentyp einer Variable.
Wenn man in der Mathematik für eine Formel eine Variable namens "a" einführt, dann kann man "a" mit allen möglichen Zahlenwerten belegen. Will man im Computerprogramm eine Variable "a" einführen (deklarieren), dann muss man dem Compiler sofort mitteilen, welchen Typ diese Variable haben soll, damit die richtige Menge Speicherplatz reserviert wird, und die Bits in diesem Speicherplatz später automatisch richtig interpretiert werden.

In C unterscheidet man fünf grundlegende Datentypen, die durch vier Zusatzbezeichnungen (Modifier) noch abgewandelt werden können.

Grundtyp
Bedeutung
Bits
Schlüsselwort
character
ein druckbares Zeichen wie z.B. ein einzelner Buchstabe
8
char
integer
eine ganzzahlige Zahl
16
int
float
eine Fließkommazahl
32
float
double eine Fließkommazahl doppelter Genauigkeit
64 (in C18 nur 32 bit)
double
void
steht für "nichts", also für keinen Wert
-
void

Die Datengrundtypen können durch das Voranstellen eines der folgenden Modifier abgewandelt werden:

Modifier
Bedeutung
Schlüsselwort
signed
Zahl kann positiv oder negativ sein
signed
unsigned
Zahl ist immer positiv
unsigned
long
"lange" Variante des Grundtyps mit mehr Bits
long
short "kurze" Variante des Grundtyps mit weniger Bits short

Zusätzlich zu diesen Grundtypen können weitere Typen definiert werden. Welche Typen der Compiler schon kennt, und wie er diese Typenbezeichnungen interpretiert, ist leider etwas vom jeweiligen Compiler abhängig.
Ein Blick in die Dokumentation lohnt also.

Nachfolgend einige Typendefinitionen des C18-Compilers und ihr jeweiliger Wertebereich:

Typ
Anzahl der Bits
Wertebereich
bit
1
0 bis 1
char
8
-128 bis 127
unsigned char
8
0 bis 255
signed char
8
-128 bis 127
int
16
-32768 bis 32767
unsigned int
16
0 bis 65535
short int
16
-32768 bis 32767
unsigned short int 16
0 bis 65535
short long
24
-8 388 608 bis 8 388 607
unsigned short long
24
0 bis 16 777 215
long int
32
-2 147 483 648 bis 2 147 483 647
unsigned long int
32
0 bis 4 294 967 295
float
32
1.17E-38 bis 6.80E+38
double
32 1.17E-38 bis 6.80E+38

Die Wertangaben bei den float-Typen sind eine abgekürzte Exponentialschreibweise. So steht "6.80E+38" für den Wert 6,8 x 1038. Das ist dann also etwa 680000000000000000000000000000000000000. Die Floattypen eignen sich dadurch für sehr große und sehr kleine Werte.

Man erkennt, dass int eigentlich schon short signed int ist.
Auch kann man sehen, dass sowohl long int wie unsigned long int als auch float jeweils 32 Bit lang sind. Sie repräsentieren aber andere Wertebereiche. Das gleiche 32-Bit-Muster in entspricht also einer ganz anderen Zahl, je nachdem, welchen Typ diese 32-Bit-Zahl darstellt. Deshalb ist es für den Compiler so wichtig, dass gleich bei der Deklaration einer Variable der Typ festgelegt wird.

Wird unsigned, short oder long ohne nachfolgenden Grundtyp verwendet, dann geht der Compiler davon aus, das der integer-Grundtyp verwendet werden soll. In der Praxis kann man also long anstelle von long int schreiben.



Variablen-Deklaration

Benötigt man eine Variable, dann deklariert man sie, indem man den Typ gefolgt vom Variablennamen schreibt:

Typ Variablenname;

Der Typ kann ein Modifier mit einem Grundtyp, oder der Bezeichner eines extra definierten Typs sein. Ein Beispiel wäre:

int alfa;

Der Compiler reserviert nun den für eine Variable dieses Typs (integer) nötigen Speicherplatz (2 Byte), und kennt ab sofort diese Variable mit dem Namen "alfa".

Man kann auch mehrere Variablen des gleichen Typs definieren, indem man eine Liste der Variablennamen verwendet:

int alfa, beta, gamma;

Wird eine Variable innerhalb einer Funktion definiert, dann wird ihr Speicherplatz am Ende der Funktion wieder freigegeben, und der Compiler vergisst dann diese Variable. So eine Variable ist eine lokale Variable. Sie ist außerhalb ihrer Funktion (also im Hauptprogramm oder in anderen Funktionen) unbekannt. Aus diesem Grunde können lokale Variablen in unterschiedlichen Funktionen auch den gleichen Namen haben, ohne das der Compiler "durcheinander kommt". (Der C18 erwartet die Deklaration lokaler Variablen immer am Anfang der Funktion - also vor der ersten Operation. Werden mitten in der Funktion noch weitere lokale Variablen deklariert, meldet C18 einen Fehler.)

Eine Variable, die außerhalb von Funktionen definiert wird ist eine globale Variable. Auf sie kann innerhalb jeder Funktion des Programms zugegriffen werden.




Wertzuweisung

Mit der Deklaration einer Variable wurde ihr Speicherplatz zugewiesen. Der (numerische) Wert dieser Variable wird durch das Bitmuster in diesem Speicherplatz bestimmt. Das ist aber noch ein zufällig dort stehender Wert.
Um nun der Variable einen Wert zuzuweisen (die Speicherzellen mit einem definierten Wert zu beschreiben) dient der "=" Operator.

Variablenname = Wert;

Der Wert kann einfach eine Zahl sein, aber auch eine Berechnung, eine Variable des gleichen Typs oder eine Funktion kann einen Wert liefern.

alfa = 100;
beta = 100 + 25;
gamma = alfa + beta;

Man muss darauf achten, dass der Wert auch dem Typ der Variablen entspricht. In einer Integer-Variablen wie alfa kann man keinen Fließkommawert speichern. Der Wert "100" ist in Ordnung, aber "5.6" oder auch "100.0" sind nicht zulässig.




Typumwandlung

In C ist es zulässig, Variablen verschiedener Typen in einem Ausdruck "miteinander  zu verrechnen". Damit das funktioniert, müssen "kleine" Typen zu "größeren" Typen erweitert werden.

int Iwert;
float Fwert;
Iwert = 5;
Fwert = 2.2;
double Dwert = Iwert + Fwert;

Im Beispiel werden eine int-Variable (Iwert) und eine float-Variable (Fwert) miteinander addiert. Dazu ist es nötig, dass erst einmal beide Variablen in den gleichen Typ überführt werden. Da der Definitionsbereich (Wertebereich) von float viel größer ist als der von int, kann man nicht jeden float-Wert in einen int-Wert wandeln. Andersherum kann aber jeder int-Wert in einer float-Variable gespeichert werden. Deshalb wird hier Iwert in float gewandelt. Nun können die beiden float-Werte addiert werden.

Das Ergebnis der Addition ist ein float-Wert. Da der aber in einer double-Variablen abgespeichert werden soll, muss er schließlich noch in double umgewandelt werden.

Diese Umwandlungen erfolgen automatisch, und führen nicht dazu, dass die deklarierten Typen von Iwert oder Fwert verändert werden. Es wird innerhalb des Ausdruckst sozusagen mit Kopien der Variablenwerte gearbeitet, und nur diese Kopien werden erweitert.

Typerweiterungen erfolgen immer in folgender Richtung:

char -> unsigned int -> long int -> unsigned long int -> float -> double -> long double


Schauen wir uns noch mal ein weiteres Beispiel an:

int alfa
char beta;
float gamma;
alfa = 5;
beta = 2;
gamma = alfa / beta;

Es soll alfa durch beta geteilt werden. Dazu müssten alfa und beta vom selben Typ sein, was sie aber noch nicht sind. Es muss der beta-Wert auf den int Typ erweitert werden. Danach sind beide Operanden vom gleichen Typ (int) und die Division wird vorgenommen. Da int ein Ganzzahltyp ist, ist auch das Ergebnis ganzzahlig, in diesem Falle also 2 (und nicht etwa 2.5). Dieses Ergebnis soll nun in gamma abgespeichert werden. Da gamma vom float Typ ist, wird auch das Rechenergebnis in float gewandelt. Aus 2 wird dadurch 2.0.

Ein numerisch exaktes Ergebnis wäre natürlich "2.5", aber dafür wäre eine Fließkommadivision nötig gewesen. Die lässt sich aber einfach erzwingen, indem man in der Division wenigstens einen float-Operanden verwendet. Man könnte als Typ für alfa oder beta den float-Typ verwenden, oder man macht es so:

int alfa
char beta;
float gamma;
alfa = 5;
beta = 2;
gamma = alfa;
gamma = gamma / beta;

Das ist natürlich etwas umständlich, weshalb man in C dafür eine "Abkürzung" geschaffen hat. Wenn man vor dem Variablennamen einen in Klammern gesetzten Typbezeichner schreibt, dann wird der Compiler angewiesen, den Typ der Variable auf den angegebenen Typ zu wandeln. Das kann dann so aussehen:

int alfa
char beta;
float gamma;
alfa = 5;
beta = 2;
gamma = (float)alfa / beta;

Da nun alfa auf float erweitert wird, muss auch beta in float gewandelt werden. Konsequenterweise wird nun eine Fließkommadivision durchgeführt, die das gewünschte Ergebnis "2.5" liefert.

Ich betone noch einmal, dass der eigentliche Typ der Variablen nicht verändert wird. Eine einmal definierte char-Variable bleibt immer char, auch wenn sie in diversen Ausdrücken mit Typenwandlungen in int und float traktiert wurde. Die Erweiterungen erfolgen immer nur an Kopien der Variablenwerte.


--> weiter zu Funktionen
nach oben

zurück zu C für PICs , C-Compiler , PIC-Prozessoren , Elektronik , Homepage



Autor: sprut
erstellt: 01.10.2007
letzte Änderung: 26.04.2015