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, das 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".
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 Variabe 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 "miteinender 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 erweiter
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 tracktiert 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: 01.10.2007