Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
c:tutorial:attribute [2013/01/12 17:23]
xin [Das Attribut unsigned] Farbwert / Intensität Satz verbessert
c:tutorial:attribute [2022/09/22 19:58] (aktuell)
Zeile 1: Zeile 1:
 ====== Attribute ====== ====== Attribute ======
  
-  * Was sind Attribute? +===== Was sind Attribute? ​=====
-  * unsigned +
-  * const +
-    * Konstanten +
-    * Read-Only-Speicherbereiche +
-    * Zeigerkonstanten +
-  * Scopes +
-  * static +
-  * volatile+
  
-===== Was sind Attribute ​=====+Von einem Attribut spricht man, wenn man einen Gegenstand genauer beschreibt. Der rote Ball ist ein Ball, der - wenn man ihn genauer beschreibt - als rot beschrieben wird. Rot ist das Attribut. Wir werden uns hier Datentypen-Attribute ​ansehen, die die Sprache C kennt. Das bedeutet, dass man der Beschreibung eines Datentyps noch eine genauere Beschreibung hinzufügen kann.
  
-Von einem Attribut spricht man, wenn man einen Gegenstand genauer beschreibt. Der rote Ball ist ein Ball, der - wenn man ihn genauer beschreibt - als rot beschrieben wird. Rot ist das Attribut. Wir werden uns hier Datentypen-Attribute ansehen, die die Sprache C kennt. Das bedeutet, dass man der Beschreibung eines Datentyps noch eine genauerer Beschreibung hinzufügen kann.+===== unsigned =====
  
-===== Das Attribut unsigned ===== +Integer-Datentypen zählen von einem maximalen negativen Wert zu einem maximalen positiven Wert. So zählt ein [[c:​type:​char]] von -128 bis 127. Einen einzelnen Farbwert (rot, grün oder blau) beschreibt man allerdings in Form seiner Intensität: Von keine Intensität (0) bis maximale Intensität (255). Um die maximale Intensität zu speichern, müssen wir uns die Zahl 255 merken und die passt nicht in ein [[c:​type:​char]]. Integerdatentypen sind per Voreinstellung immer ''​signed''​. ''​signed''​ ist übrigens auch ein Schlüsselwort der Sprache C, aber es benutzt eigentlich keiner, weil es die Voreinstellung ist.
- +
-Integer-Datentypen zählen von einem maximalen negativen Wert zu einem maximalen positiven Wert. So zählt ein [[c:​type:​char]] von -128 bis 127. Einen einzelnen Farbwert (rot, grün oder blau) beschreibt man allerdings in Form seiner Intensität, von keine Intensität (0) bis maximale Intensität (255). Um die maximale Intensität zu speichern, müssen wir uns die Zahl 255 merken und die passt nicht in ein [[c:​type:​char]]. Integerdatentypen sind per Voreinstellung immer ''​signed''​. ''​signed''​ ist übrigens auch ein Schlüsselwort der Sprache C, aber es benutzt eigentlich keiner, weil es die Voreinstellung ist.+
 Wenn wir bei einem ''​signed char''​ also die ungebrauchten negativen Zahlen den positiven Zahlen hinzufügen könnten, dann hätten wir 127 (maximaler positiver Wert) + 128 (maximaler negativer Wert) = 255 als größte Zahl - mit der 0 also zusammen 256 unterschiedliche Werte, die wir mit einem ''​unsigned char''​ ausdrücken können. Wenn wir bei einem ''​signed char''​ also die ungebrauchten negativen Zahlen den positiven Zahlen hinzufügen könnten, dann hätten wir 127 (maximaler positiver Wert) + 128 (maximaler negativer Wert) = 255 als größte Zahl - mit der 0 also zusammen 256 unterschiedliche Werte, die wir mit einem ''​unsigned char''​ ausdrücken können.
  
-Uns so sehen Attribute im Quelltext aus - man schreibt einfach ''​unsigned''​ **vor** den Datentyp.+Und so sehen Attribute im Quelltext aus - man schreibt einfach ''​unsigned''​ **vor** den Datentyp.
 <code cpp> <code cpp>
 char           ​c; ​  // Wertebereich -128..127 - entspricht signed char char           ​c; ​  // Wertebereich -128..127 - entspricht signed char
Zeile 41: Zeile 31:
 |                                        |                  |                     | unsigned ​  ​| ​                    ​0 ​                        | (18.446.744.073.709.551.615) ​                  | |                                        |                  |                     | unsigned ​  ​| ​                    ​0 ​                        | (18.446.744.073.709.551.615) ​                  |
 \\ \\
-Die angegebenen Größen sind Mindestgrößen! Die wirklichen Größen finden sich in der Headerdatei ''​[[c:​lib:​limits:​|limit.h]]''​. Gerade für den Datentyp ''​[[c:​type:​int]]''​ gilt heute eher, dass er 32 Bit breit ist und damit ''​[[c:​type:​long]]''​ entspricht. Aber C garantiert das für ''​[[c:​type:​int]]''​ nicht, sondern nur 16 Bit, wie bei ''​[[c:​type:​short]]''​.+Die angegebenen Größen sind Mindestgrößen! Die wirklichen Größen finden sich in der Headerdatei ''​[[c:​lib:​limits:​|limits.h]]''​. Gerade für den Datentyp ''​[[c:​type:​int]]''​ gilt heute eher, dass er 32 Bit breit ist und damit ''​[[c:​type:​long]]''​ entspricht. Aber C garantiert das für ''​[[c:​type:​int]]''​ nicht, sondern nur 16 Bit, wie bei ''​[[c:​type:​short]]''​.
  
  
-Auch wenn sich ''​int''​ kürzer schreibt als ''​unsigned int'',​ so steckt für den Compiler, wie auch für den Menschen eine zusätzliche Information in diesem Datentyp. Auf ein Array darf man meistens nur mit positiven Werten zugreifen. Möchte man auf ein Array mit 256 Elementen zugreifen, so darf der Index selbst kein char sein, sondern muss mindestens ein ''​unsigned ​char''​ sein, da man ansonsten nicht hinter den Beginn des Arrays zugreift, sondern ​von 128 Elementen vor dem Arraybeginn ​bis 127 Elemente nach dem ArraybeginnDie hinteren ​128 Elemente erreicht man so nicht, während man gleichzeitig ​128 Elemente ​erreicht, auf die man nicht zugreifen darf. Dies zu vergessen ist ein beliebter Anfängerfehler. Es ist auch nicht in der Verantwortung der Programmiersprache, ​den Entwickler zu erklären, was er tun und lassen darf, sondern Du musst dem Computer erklären, was er tun soll. +Auch wenn sich ''​int''​ kürzer schreibt als ''​unsigned int'',​ so steckt für den Compiler, wie auch für den Menscheneine zusätzliche Information in diesem Datentyp. Auf ein Array darf man meistens nur mit positiven Werten zugreifen. Möchte man auf ein Array mit 256 Elementen zugreifen, so darf der Index selbst kein ''​char''​ sein, weil dieser Typ wie bereits erwähnt nur einen Wertebereich ​von -128 bis 127 abdecktObwohl das 256 verschiedene Werte sind, können die höheren ​128 der 256 Elemente ​im Array nicht erreicht werden. Gleichzeitig ​erreicht man über den negativen Wertebereich ​128 Elemente, auf die man eigentlich ​nicht zugreifen darf. Der Index muss daher mindestens ein ''​unsigned char''​ mit einem Wertebereich von 0 bis 255 sein, um auf alle 256 gültigen Elemente des Arrays zuzugreifen. Dies zu vergessen ist ein beliebter Anfängerfehler. Es ist auch nicht in der Verantwortung der Programmiersprache, ​dem Entwickler zu erklären, was er tun und lassen darf, sondern Du musst dem Computer erklären, was er tun soll. 
-Bei einem Array, bei dem Du hinter den Start zugreifen ​möchtest, benutze also möglichst Variablen eines Integertyps,​ der mit dem Attribut ''​unsigned''​ erweitert wurde.+Bei einem Array, bei dem Du nur positive Indizes verwenden ​möchtest, benutze also möglichst Variablen eines Integertyps,​ der mit dem Attribut ''​unsigned''​ erweitert wurde.
  
 Schauen wir uns das an: Schauen wir uns das an:
Zeile 55: Zeile 45:
 </​code>​ </​code>​
  
-Verwendest Du für ''​index''​ eine unsigned-int-Variable ​kann Dir dies nicht passieren.+Verwendest Du für ''​index''​ eine Variable vom Typ ''​unsigned int'', ​kann Dir dies nicht passieren.
  
 Dies solltest Du grundsätzlich tun, wenn Werte nicht negativ werden können. Nehmen wir die Anzahl Daten, zum Beispiel die Größe von Arbeitsspeicher. Man kann keine negative Menge Speicher anfordern: ''​unsigned''​ verwenden! Dies solltest Du grundsätzlich tun, wenn Werte nicht negativ werden können. Nehmen wir die Anzahl Daten, zum Beispiel die Größe von Arbeitsspeicher. Man kann keine negative Menge Speicher anfordern: ''​unsigned''​ verwenden!
 Nehmen wir die Länge eines Strings: Ein String kann nicht kürzer als 0 Zeichen lang sein: ''​unsigned''​ verwenden! Nehmen wir die Länge eines Strings: Ein String kann nicht kürzer als 0 Zeichen lang sein: ''​unsigned''​ verwenden!
  
-Die bessere Version unserer strLength()-Routine sieht also so aus:+Die bessere Version unserer ​''​strLength()''​-Routine sieht also so aus:
 <code cpp> <code cpp>
 unsigned int strLength( char * string ) unsigned int strLength( char * string )
Zeile 78: Zeile 68:
 ===== const ===== ===== const =====
  
-Const und [[cpp:​const:​start|in C++ "​Const-Correctness"​]] sind sehr wichtige Themen, um die Qualität von Software zu erhöhen. Wie unsigned ist const eine zusätzliche Garantie, die man vergibt. Die Garantie bei const lautet, dass sich der Wert nicht verändern wird und das ist eine sehr wichtige Eigenschaft.+''​const'' ​und [[cpp:​const:​start|in C++ "​Const-Correctness"​]] sind sehr wichtige Themen, um die Qualität von Software zu erhöhen. Wie ''​unsigned'' ​ist ''​const'' ​eine zusätzliche Garantie, die man vergibt. Die Garantie bei ''​const'' ​lautet, dass sich der Wert nicht verändern wird und das ist eine sehr wichtige Eigenschaft.
  
 ==== Konstanten ==== ==== Konstanten ====
Zeile 131: Zeile 121:
 const.c:12: error: assignment of read-only variable ‘pi’</​code>​ const.c:12: error: assignment of read-only variable ‘pi’</​code>​
  
-Da der Compiler nun versteht, dass er pi nicht überschreiben darf, kann er diesen Fehler auch erkennen.+Da der Compiler nun versteht, dass er ''​pi'' ​nicht überschreiben darf, kann er diesen Fehler auch erkennen.
  
 Wir korrigieren die Methode ''​circumference'':​ Wir korrigieren die Methode ''​circumference'':​
Zeile 165: Zeile 155:
 </​code> ​ </​code> ​
  
-Aber folgendes funktioniert ist gefährlich:​+Aber folgendes funktioniert ​und ist gefährlich:​
 <code cpp> <code cpp>
 #include <​stdio.h>​ #include <​stdio.h>​
Zeile 175: Zeile 165:
    ​return 0;    ​return 0;
 }</​code> ​ }</​code> ​
-Der gcc (Version 4.4.5) akzeptiert das, aber das Verhalten wird sich ändern. Früher hat man die Fehlererkennung durch den Compiler nicht so ernst genommen und so entstand viel Quelltext, der solche Text-Zuweisungen enthält. Damit diese weiterhin kompilieren und nicht urplötzlich tausende Zeilen geändert werden müssen, akzeptiert der gcc derartiges. + 
-In C++ ist const mächtiger und damit auch weiter verbreitet: der g++ meckert schonmal: +Der gcc (Version 4.9.1) akzeptiert das, aber das Verhalten wird sich ändern. Früher hat man die Fehlererkennung durch den Compiler nicht so ernst genommen und so entstand viel Quelltext, der solche Text-Zuweisungen enthält. Damit diese weiterhin kompilieren und nicht urplötzlich tausende Zeilen geändert werden müssen, akzeptiert der gcc derartiges. 
-<​code>​constptr.c:​5:​ warning: ​initialization discards qualifiers from pointer target type</​code>​+In C++ ist ''​const'' ​mächtiger und damit auch weiter verbreitet: der g++ meckert schonmal: 
 +<​code>​constptr.c:​5:21: warning: ​ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]</​code>​
  
 Was passiert da eigentlich? Der Text "​proggen.org"​ ist im ausführbaren Programm gespeichert. Die Variable ''​arrayVariable''​ ist eine Variable, deren Größe entsprechend der Länge des Textes "​proggen.org"​ gewählt wird. Und dann wird der Text "​proggen.org"​ in die Variable kopiert. In unserem Array dürfen wir dann den Text nach belieben ändern. Den Text "​proggen.org"​ dürfen wir aber nicht ändern, denn sonst würde bei jedem Aufruf von "​function"​ die Variable ''​arrayVariable''​ verändert sein. Was passiert da eigentlich? Der Text "​proggen.org"​ ist im ausführbaren Programm gespeichert. Die Variable ''​arrayVariable''​ ist eine Variable, deren Größe entsprechend der Länge des Textes "​proggen.org"​ gewählt wird. Und dann wird der Text "​proggen.org"​ in die Variable kopiert. In unserem Array dürfen wir dann den Text nach belieben ändern. Den Text "​proggen.org"​ dürfen wir aber nicht ändern, denn sonst würde bei jedem Aufruf von "​function"​ die Variable ''​arrayVariable''​ verändert sein.
Zeile 193: Zeile 184:
    ​return 0;    ​return 0;
 }</​code> ​ }</​code> ​
-kompilieren das und führen es aus: + 
-<​code>​xin@trinity:​~/​proggen.org/​tutorial$ ./​a.out ​+Wir kompilieren das und führen es aus: 
 +<​code>​$ ./​a.out ​
 Speicherzugriffsfehler Speicherzugriffsfehler
 </​code>​ </​code>​
  
-Ein String, der in Anführungszeichen steht ist eine Zeiger auf konstante Buchstaben: char const *.+Ein String, der in Anführungszeichen steht ist eine Zeiger auf konstante Buchstaben: ​''​char const *''​ 
 Der korrekte Quelltext sieht also so aus: Der korrekte Quelltext sieht also so aus:
 <code cpp> <code cpp>
Zeile 214: Zeile 207:
 Hier kann der Compiler nun auch warnen: Hier kann der Compiler nun auch warnen:
 <​code>​ <​code>​
-xin@trinity:​~/​proggen.org/​tutorial$ gcc const.c ​+$ gcc const.c ​
 constptr.c: In function ‘main’: constptr.c: In function ‘main’:
 constptr.c:​7:​ error: assignment of read-only location ‘*pointer’ constptr.c:​7:​ error: assignment of read-only location ‘*pointer’
Zeile 221: Zeile 214:
 Interessant ist das nun, wenn Zeiger an Funktionen übergeben werden und man sicher sein möchte, dass die Daten von diesen Funktionen nicht verändert werden - zum Beispiel, weil die Daten nicht verändert werden dürfen. Interessant ist das nun, wenn Zeiger an Funktionen übergeben werden und man sicher sein möchte, dass die Daten von diesen Funktionen nicht verändert werden - zum Beispiel, weil die Daten nicht verändert werden dürfen.
  
-Schauen wir uns eine Funktion wie strLength ​nocheinmal ​an, die keine Daten ändert und eine weitere Funktion, die den übergebenen String mit Punkten überschreibt:​+Schauen wir uns eine Funktion wie ''​strLength''​ nochmals ​an, die keine Daten ändert und eine weitere Funktion, die den übergebenen String mit Punkten überschreibt:​
 <code cpp> <code cpp>
 #include <​stdio.h>​ #include <​stdio.h>​
Zeile 260: Zeile 253:
 Nach dem Kompilieren erhält man folgende Ausgabe: Nach dem Kompilieren erhält man folgende Ausgabe:
 <​code>​ <​code>​
-xin@trinity:​~/​proggen.org/​tutorial$ ./​a.out ​+$ ./​a.out ​
 Der Text ist 11 Zeichen lang. Der Text ist 11 Zeichen lang.
 Speicherzugriffsfehler Speicherzugriffsfehler
 </​code>​ </​code>​
  
-Die Funktion strLength() funktioniert,​ da wir glücklicherweise nichts ändern. Anschließend wird ausgegeben, wie lang der Text ist und das Programm stürzt in der Funktion dots() ab und stellt klar, dass wir mit konstanten Strings arbeiten. Beide Funktionen bekommen nun statt ''​char *''​ einen ''​char const *''​ übergeben:+Die Funktion ​''​strLength()'' ​funktioniert,​ da wir glücklicherweise nichts ändern. Anschließend wird ausgegeben, wie lang der Text ist und das Programm stürzt in der Funktion ​''​dots()'' ​ab und stellt klar, dass wir mit konstanten Strings arbeiten. Beide Funktionen bekommen nun statt ''​char *''​ einen ''​char const *''​ übergeben:
  
 <code cpp> <code cpp>
Zeile 310: Zeile 303:
 funcconst.c:​9:​ error: assignment of read-only location ‘*(string + (long unsigned int)pos)’</​code>​ funcconst.c:​9:​ error: assignment of read-only location ‘*(string + (long unsigned int)pos)’</​code>​
  
-Die Funktion dots() möchte Änderungen durchführen,​ aber das darf sie bei einem ''​char const *''​ nicht. Also muss hier definitiv ein ''​char *''​ übergeben werden, also ein Pointer auf veränderbare ''​chars'':​+Die Funktion ​''​dots()'' ​möchte Änderungen durchführen,​ aber das darf sie bei einem ''​char const *''​ nicht. Also muss hier definitiv ein ''​char *''​ übergeben werden, also ein Pointer auf veränderbare ''​chars'':​
  
 Also zurück zu Also zurück zu
Zeile 317: Zeile 310:
 Auch das lässt sich nicht kompilieren:​ Auch das lässt sich nicht kompilieren:​
 <​code>​ <​code>​
-xin@trinity:​~/​proggen.org/​tutorial$ gcc funcconst.c ​+$ gcc funcconst.c ​
 funcconst.c:​ In function ‘main’: funcconst.c:​ In function ‘main’:
 funcconst.c:​31:​ warning: passing argument 1 of ‘dots’ discards qualifiers from pointer target type funcconst.c:​31:​ warning: passing argument 1 of ‘dots’ discards qualifiers from pointer target type
Zeile 325: Zeile 318:
 Die Funktion ''​dots()''​ eignet sich einfach nicht für konstante Zeiger und der C-Compiler **warnt** uns, dass hier etwas verkehrt läuft. Der C++ Compiler wird deutlicher und quittiert das Programm mit einem **error**: Das Programm ist mit g++ nicht kompilierbar. Die Funktion ''​dots()''​ eignet sich einfach nicht für konstante Zeiger und der C-Compiler **warnt** uns, dass hier etwas verkehrt läuft. Der C++ Compiler wird deutlicher und quittiert das Programm mit einem **error**: Das Programm ist mit g++ nicht kompilierbar.
 <​code>​ <​code>​
-xin@trinity:​~/​proggen.org/​tutorial$ g++ funcconst.c ​+$ g++ funcconst.c ​
 funcconst.c:​ In function ‘int main()’: funcconst.c:​ In function ‘int main()’:
 funcconst.c:​31:​ error: invalid conversion from ‘const char*’ to ‘char*’ funcconst.c:​31:​ error: invalid conversion from ‘const char*’ to ‘char*’
Zeile 335: Zeile 328:
 int main( void ) int main( void )
 { {
-  char const array[] = "​proggen.org"; ​+  char array[] = "​proggen.org"; ​
   unsigned int length = strLength( array );   unsigned int length = strLength( array );
    
Zeile 350: Zeile 343:
 Nach dem kompilieren erhalten wir als Ausgabe: Nach dem kompilieren erhalten wir als Ausgabe:
 <​code>​ <​code>​
-xin@trinity:​~/​proggen.org/​tutorial$ ./​a.out ​+$ ./​a.out ​
 Der Text ist 11 Zeichen lang. Der Text ist 11 Zeichen lang.
 Der Text ist nach dots() nun: ........... Der Text ist nach dots() nun: ...........
 </​code>​ </​code>​
  
-Wir haben bei ''​strLength()''​ die Garantie, dass der übergebene String nicht verändert wird, wir können also auch Arrays als konstante Werte übergeben. Die Funktion dots() erlaubt keine konstanten Arrays, da sie die Daten ändern möchte. Der Compiler ist in der Lage Fehler zu erkennen und zu melden. Lies Dir auch Warnings durch, Code mit Warnings ist grundsätzlich unzureichend!+Wir haben bei ''​strLength()''​ die Garantie, dass der übergebene String nicht verändert wird, wir können also auch Arrays als konstante Werte übergeben. Die Funktion ​''​dots()'' ​erlaubt keine konstanten Arrays, da sie die Daten ändern möchte. Der Compiler ist in der Lage Fehler zu erkennen und zu melden. Lies Dir auch Warnings durch, Code mit Warnings ist grundsätzlich unzureichend!
  
-**Grundregel**:​ Wenn Du qualitative Software herstellen möchtest, sorge dafür, dass Zeiger grundsätzlich auf konstante Daten zeigen, außer Du musst in dieser Funktion Daten ändern. Das zieht sich durch das ganze Programm durch: ​eine Funktion, die einen konstanten Parameter erhalten hat, kann ihn nicht an eine Funktion übergeben, die eine variablen Speicherbereich benötigt.+**Grundregel**:​ Wenn Du qualitative Software herstellen möchtest, sorge dafür, dass Zeiger grundsätzlich auf konstante Daten zeigen, außer Du musst in dieser Funktion Daten ändern. Das zieht sich durch das ganze Programm durch: ​Eine Funktion, die einen konstanten Parameter erhalten hat, kann ihn nicht an eine Funktion übergeben, die eine variablen Speicherbereich benötigt.
  
 ==== Zeigerkonstanten ==== ==== Zeigerkonstanten ====
Zeile 366: Zeile 359:
 <code cpp>char const * ptr = "​proggen.org";</​code>​ <code cpp>char const * ptr = "​proggen.org";</​code>​
  
-Das ist in seiner Bedeutung gleichwertig,​ es macht also für das Programm ​keinen Unterschied. Ich empfehle trotzdem const nach dem Datentyp zu schreiben, um Verwechslungen auszuschließen. Schauen wir uns folgende Zeigervariablen an:+Das ist in seiner Bedeutung gleichwertig,​ es macht also für den Compiler ​keinen Unterschied. Ich empfehle trotzdem ​''​const'' ​nach dem Datentyp zu schreiben, um Verwechslungen auszuschließen. Schauen wir uns folgende Zeigervariablen an:
 <code cpp> <code cpp>
 char const * ptr; char const * ptr;
Zeile 373: Zeile 366:
 ptr = "Hallo Welt"; ptr = "Hallo Welt";
 </​code>​ </​code>​
 +
 Wir können hier den Wert der Zeigervariablen änderen, mal zeigt sie auf das eine Array mit konstanten chars, mal auf ein anderes Array mit konstanten chars. Man kann aber auch Zeigerkonstanten definieren, die man - wie alle anderen Konstanten - sofort mit einem Wert initialisieren,​ also beschreiben,​ muss. Wir können hier den Wert der Zeigervariablen änderen, mal zeigt sie auf das eine Array mit konstanten chars, mal auf ein anderes Array mit konstanten chars. Man kann aber auch Zeigerkonstanten definieren, die man - wie alle anderen Konstanten - sofort mit einem Wert initialisieren,​ also beschreiben,​ muss.
  
Zeile 387: Zeile 381:
 denn hier ist der Datentyp nun doppelt konstant und der Zeiger bleibt variabel. denn hier ist der Datentyp nun doppelt konstant und der Zeiger bleibt variabel.
  
-Eine Zeigerkonstante muss nicht zwangsläufig auf konstante Daten zeigen: ​die Konstante kann auch durchaus auf variable Daten zeigen:+Eine Zeigerkonstante muss nicht zwangsläufig auf konstante Daten zeigen: ​Die Konstante kann auch durchaus auf variable Daten zeigen:
 <code cpp> <code cpp>
 char array[] = "Hallo proggen.org";​ char array[] = "Hallo proggen.org";​
Zeile 398: Zeile 392:
 Dies gilt auch in Hinblick zu C++, wo es konstante Methoden gibt. Würde man hier ''​const''​ vor die Signatur schreiben, sähe das so aus: Dies gilt auch in Hinblick zu C++, wo es konstante Methoden gibt. Würde man hier ''​const''​ vor die Signatur schreiben, sähe das so aus:
 <code cpp>​const char * method()</​code>​ <code cpp>​const char * method()</​code>​
-Das geht nicht - das besagt, dass ein Zeiger auf konstante ​Chars zurückgegeben wird. Auch hier wird ''​const''​ hinter die betreffende Methode geschrieben:​+Das geht nicht - das besagt, dass ein Zeiger auf konstante ​Zeichen ​zurückgegeben wird. Auch hier wird ''​const''​ hinter die betreffende Methode geschrieben:​
 <code cpp>char * method() const</​code>​ <code cpp>char * method() const</​code>​
  
Zeile 431: Zeile 425:
 }</​code>​ }</​code>​
  
-Globale Variablen existieren von Programmbeginn bis Programmende. Im Prinzip geht es der Variablen ''​lokal''​ nicht  +Globale Variablen existieren von Programmbeginn bis Programmende. Im Prinzip geht es der Variablen ''​lokal''​ nicht anders, wird sie doch am Anfang der Funktion ''​main()''​ erzeugt und am Ende von main() vernichtet. Dennoch lebt die Variable ''​global''​ ein klein wenig länger als ''​lokal'':​
-anders, wird sie doch am Anfang der Funktion ''​main()''​ erzeugt und am Ende von main() vernichtet. Dennoch lebt die Variable ''​global''​ ein klein wenig länger als ''​lokale'':​+
  
 Wann immer wir eine geschweifte Klammer öffnen um Anweisungen zusammen zu fassen, öffnen wir einen sogenannten **Scope**. Das ist das Umfeld, in dem die Variablen existieren - sobald die Klammer geschlossen wird, wird auch die Variable zerstört: Wann immer wir eine geschweifte Klammer öffnen um Anweisungen zusammen zu fassen, öffnen wir einen sogenannten **Scope**. Das ist das Umfeld, in dem die Variablen existieren - sobald die Klammer geschlossen wird, wird auch die Variable zerstört:
Zeile 466: Zeile 459:
 Man kann immer auf die Variablen im eigenen Scope und den darüber angeordneten Scopes zugreifen. Globale Variablen liegen über allen Scopes. Man kann immer auf die Variablen im eigenen Scope und den darüber angeordneten Scopes zugreifen. Globale Variablen liegen über allen Scopes.
  
-Scopes beschränken also die Lebensdauer von Variablen, globale Variablen liegen gewissermaßen in einem übergeordneten Scope, der erst zerstört wird, nachdem die Funktion main() verlassen wurde. Es gibt ein Leben nach main() - und genauso eins davor, zum Beispiel, um die Variablen anzulegen. Unser Programm arbeitet also bereits, bevor main() gerufen wird. main() wird erst gestartet, nachdem das globale Scope eingerichtet wurde und alle anderen erforderlichen Vorbereitungen durchgeführt sind.+Scopes beschränken also die Lebensdauer von Variablen, globale Variablen liegen gewissermaßen in einem übergeordneten Scope, der erst zerstört wird, nachdem die Funktion ​''​main()'' ​verlassen wurde. Es gibt ein Leben nach ''​main()'' ​- und genauso eins davor, zum Beispiel, um die Variablen anzulegen. Unser Programm arbeitet also bereits, bevor ''​main()'' ​gerufen wird. ''​main()'' ​wird erst gestartet, nachdem das globale Scope eingerichtet wurde und alle anderen erforderlichen Vorbereitungen durchgeführt sind.
  
 ===== static ===== ===== static =====
  
-Das Attribut static bewirkt, dass eine beschriebene Variable eine statische Adresse erhält, die sich nicht ändert. Das bedeutet zum einen, dass es sich um genau eine Variable handelt - klar, sagst Du, wieviele auch sonst?+Das Attribut ​''​static'' ​bewirkt, dass eine beschriebene Variable eine statische Adresse erhält, die sich nicht ändert. Das bedeutet zum einen, dass es sich um genau eine Variable handelt - klar, sagst Du, wieviele auch sonst?
  
-Nun wir haben gerade gelesen, dass es Scopes gibt und wann immer eine Funktion gerufen wird, Variablen erzeugt und am Schluss wieder vernichtet werden. Das ist aber nicht immer gewünscht. Stellen wir uns eine Funktion count() vor, die zählt, wie oft sie aufgerufen wurde:+Nun wir haben gerade gelesen, dass es Scopes gibt und wann immer eine Funktion gerufen wird, Variablen erzeugt und am Schluss wieder vernichtet werden. Das ist aber nicht immer gewünscht. Stellen wir uns eine Funktion ​''​count()'' ​vor, die zählt, wie oft sie aufgerufen wurde:
  
 <code cpp> <code cpp>
Zeile 485: Zeile 478:
 </​code>​ </​code>​
  
-Die Variable counter wird jedesmal neu angelegt, mit 0 initialisiert und anschließend um 1 hochgezählt. Die Funktion gibt also grundsätzlich 1 zurück. So war das nicht gedacht. Wir brauchen also eine Variable, die am Ende des Funktionsaufrufs nicht einfach verschwindet. Wir wissen, dass der globale Scope erst nach dem Verlassen von main() zerstört wird, der Wert der Variablen also erhalten bleibt:+Die Variable ​''​counter'' ​wird jedesmal neu angelegt, mit 0 initialisiert und anschließend um 1 hochgezählt. Die Funktion gibt also grundsätzlich 1 zurück. So war das nicht gedacht. Wir brauchen also eine Variable, die am Ende des Funktionsaufrufs nicht einfach verschwindet. Wir wissen, dass der globale Scope erst nach dem Verlassen von main() zerstört wird, der Wert der Variablen also erhalten bleibt:
  
 <code cpp> <code cpp>
Zeile 499: Zeile 492:
  
 So funktioniert es! So funktioniert es!
-Aber dennoch ist die Lösung eher unschön, denn jeder kann die Variable counter verändern - wir wollen ein Problem lösen, müssen aber zwei Identifier, nämlich count und counter, in den globalen Namensraum setzen. Vermutlich kommt es schnell zu Verwechslungen,​ ob die Funktion nun count() oder counter() heißt. Es wäre also schön, wenn die Variable ''​counter''​ außerhalb der Funktion ''​count()''​ nicht sichtbar wäre. +Aber dennoch ist die Lösung eher unschön, denn jeder kann die Variable ​''​counter'' ​verändern - wir wollen ein Problem lösen, müssen aber zwei Identifier, nämlich ​''​count'' ​und ''​counter''​, in den globalen Namensraum setzen. Vermutlich kommt es schnell zu Verwechslungen,​ ob die Funktion nun ''​count()'' ​oder ''​counter()'' ​heißt. Es wäre also schön, wenn die Variable ''​counter''​ außerhalb der Funktion ''​count()''​ nicht sichtbar wäre. 
-Und hier kommt static das erste mal ins Spiel. Alle globalen Variablen sind statisch, also einmalig an einer festen Adresse.+Und hier kommt ''​static'' ​das erste Mal ins Spiel. Alle globalen Variablen sind statisch, also einmalig an einer festen Adresse.
  
 Mit dem Schlüsselwort ''​static''​ entkoppelt man eine Variable aus dem Scope, sie wird nach dem Verlassen des Scopes nicht zerstört. Sie landet im Speicherbereich des globalen Scopes, ist allerdings dort nicht sichtbar. Sichtbar bleibt sie nur im Scope, in dem sie angemeldet wurde. Mit dem Schlüsselwort ''​static''​ entkoppelt man eine Variable aus dem Scope, sie wird nach dem Verlassen des Scopes nicht zerstört. Sie landet im Speicherbereich des globalen Scopes, ist allerdings dort nicht sichtbar. Sichtbar bleibt sie nur im Scope, in dem sie angemeldet wurde.
-Die Initialisierung einer statischen ​Variablen ​wird dann ausgeführt,​ wenn der Programmablauf erstmals an ihrer Definition ​der Variable ​vorbeikommt. Liegt diese Variable also in einer Funktion, wird sie beim ersten Aufruf der Funktion angelegt und initialisiert. **Die Initialisierung einer statischen Variable ist einmalig und wird nicht bei jedem Aufruf der Funktion durchgeführt**.+Die Initialisierung einer statischen ​Variable ​wird dann ausgeführt,​ wenn der Programmablauf erstmals an ihrer Definition vorbeikommt. Liegt diese Variable also in einer Funktion, wird sie beim ersten Aufruf der Funktion angelegt und initialisiert. **Die Initialisierung einer statischen Variable ist einmalig und wird nicht bei jedem Aufruf der Funktion durchgeführt**.
  
 <code cpp> <code cpp>
Zeile 517: Zeile 510:
 }                         // counter wird nicht vernichtet! }                         // counter wird nicht vernichtet!
  
-void main()+int main( void )
 {                         // counter ist nicht sichtbar {                         // counter ist nicht sichtbar
   printf( "​Zähle:​ %d\n", count() );   printf( "​Zähle:​ %d\n", count() );
Zeile 533: Zeile 526:
 ===== volatile ===== ===== volatile =====
  
-volatile bedeutet soviel wie '​flüchtig'​ oder '​sprunghaft'​. C wird gerne in der hardwarenahen Programmierung verwendet und wenn auf einen Speicherbereich mehrere Prozessoren schreibend zugreifen können, so darf man nicht davon ausgehen, dass diese Speicherbereiche unverändert bleiben, wenn man selbst keine Änderung daran vornimmt.+''​volatile'' ​bedeutet soviel wie '​flüchtig'​ oder '​sprunghaft'​. C wird gerne in der hardwarenahen Programmierung verwendet und wenn auf einen Speicherbereich mehrere Prozessoren schreibend zugreifen können, so darf man nicht davon ausgehen, dass diese Speicherbereiche unverändert bleiben, ​auch wenn man selbst keine Änderung daran vornimmt.
  
 Prozessoren können viel schneller rechnen, wenn sie Variablen in den Prozessorkern laden und dann damit arbeiten. Das wissen die Compilerhersteller und so wird bei Schleifen wie Prozessoren können viel schneller rechnen, wenn sie Variablen in den Prozessorkern laden und dann damit arbeiten. Das wissen die Compilerhersteller und so wird bei Schleifen wie
Zeile 543: Zeile 536:
   result = result * i;   result = result * i;
 </​code>​ </​code>​
-i nicht in grundsätzlich in jedem Schritt auf den Speicher zurückgeschrieben,​ sondern nur einmal erst nachdem die Schleife durchgelaufen ist. Das spart Zeit, da der Prozessorkern sehr viel schneller ist, als der Arbeitsspeicher. +''​i'' ​nicht grundsätzlich in jedem Schritt auf den Speicher zurückgeschrieben,​ sondern nur einmal erst nachdem die Schleife durchgelaufen ist. Das spart Zeit, da der Prozessorkern sehr viel schneller ist, als der Arbeitsspeicher. 
-Wenn nun ein anderer Prozessor darüber informiert werden muss, wo die Schleife gerade dran ist, dann muss die Variable i aber immer wieder in den Speicher zurückgeschrieben werden, damit der andere Prozessor den Wert aus dem Speicher auslesen kann.+Wenn nun ein anderer Prozessor darüber informiert werden muss, wo die Schleife gerade dran ist, dann muss die Variable ​''​i'' ​aber immer wieder in den Speicher zurückgeschrieben werden, damit der andere Prozessor den Wert aus dem Speicher auslesen kann.
 Um hier eine Optimierung zu verhindern und dem C Compiler zu sagen, dass er den Wert in den Speicher schreiben muss, benutzt man ''​volatile'':​ Um hier eine Optimierung zu verhindern und dem C Compiler zu sagen, dass er den Wert in den Speicher schreiben muss, benutzt man ''​volatile'':​
 <code cpp> <code cpp>
Zeile 554: Zeile 547:
 </​code>​ </​code>​
  
-volatile ist wie gesagt in der Hardwareprogrammierung wichtig und für dieses Tutorial hier nur vollständigkeitshalber aufgeführt.+''​volatile'' ​ist wie gesagt in der Hardwareprogrammierung wichtig und für dieses Tutorial hier nur vollständigkeitshalber aufgeführt.
  
 ====== Ziel dieser Lektion ====== ====== Ziel dieser Lektion ======