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.
- So sollte eigentlich "integer" der Datenwortbreite des
Prozessors
entsprechen und ist so auf jedem Rechner etwas anders definiert.
Allerdings ist im C18-Compiler der integer-Typ 16 Bit lang,
obwohl
dieser PIC ein 8-Bit Datenbus hat.
- Der double Typ ist normalerweise 64 Bit lang. In C18 ist aber
double mit float identisch, also nur 32 Bit lang.
- In C18 gibt es zusätzliche 24 Bit integer Typen mit dem
originellen Modifier "short long".
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
zurück
zu C für PICs , C-Compiler , PIC-Prozessoren , Elektronik , Homepage
Autor: sprut
erstellt: 01.10.2007
letzte Änderung: 26.04.2015