Podcast
Questions and Answers
Welche Aussage beschreibt am besten die Parameterübergabe in C?
Welche Aussage beschreibt am besten die Parameterübergabe in C?
- Die Parameterübergabe in C verwendet standardmäßig 'call-by-reference', wobei die Originalwerte der Argumente verändert werden können.
- Die Parameterübergabe in C ist immer 'call-by-value', wobei eine Kopie des Arguments an die Funktion übergeben wird. (correct)
- Die Parameterübergabe in C ist 'call-by-name', wobei der Name des Arguments an die Funktion übergeben wird.
- In C kann sowohl 'call-by-value' als auch 'call-by-reference' explizit durch den Programmierer bestimmt werden.
Wie kann 'call-by-reference' in C simuliert werden?
Wie kann 'call-by-reference' in C simuliert werden?
- Durch die Verwendung von statischen Variablen innerhalb der Funktion.
- Durch das Nutzen von Makros, die den Code der aufrufenden Funktion direkt manipulieren.
- Durch die Übergabe von Zeigern auf Variablen, wodurch die Funktion die Speicheradresse der Variable erhält. (correct)
- Durch die Verwendung globaler Variablen, die in der Funktion verändert werden.
Was ist die Hauptfunktion eines Funktionsprototyps in C?
Was ist die Hauptfunktion eines Funktionsprototyps in C?
- Den Speicher für die Parameter der Funktion reservieren.
- Die Definition der Funktion zu verstecken, um die Implementierung zu schützen.
- Dem Compiler die Signatur einer Funktion bekannt zu machen, die später definiert wird. (correct)
- Einen alternativen Einstiegspunkt für das Programm definieren.
Wann ist die Deklaration eines Funktionsprototyps besonders wichtig?
Wann ist die Deklaration eines Funktionsprototyps besonders wichtig?
Was ist der Unterschied zwischen Funktionskommentaren und Implementierungskommentaren?
Was ist der Unterschied zwischen Funktionskommentaren und Implementierungskommentaren?
Wo werden Funktionen typischerweise im Bezug zum Hauptprogramm definiert?
Wo werden Funktionen typischerweise im Bezug zum Hauptprogramm definiert?
Was passiert, wenn eine lokale Variable denselben Namen wie eine globale Variable hat?
Was passiert, wenn eine lokale Variable denselben Namen wie eine globale Variable hat?
Was kennzeichnet die Lebensdauer einer lokalen Variable in C?
Was kennzeichnet die Lebensdauer einer lokalen Variable in C?
Welche Aussage bezüglich der Initialisierung von Auto-Variablen ist korrekt?
Welche Aussage bezüglich der Initialisierung von Auto-Variablen ist korrekt?
Was passiert mit dem Speicherplatz einer lokalen Variable, wenn der Block, in dem sie definiert ist, verlassen wird?
Was passiert mit dem Speicherplatz einer lokalen Variable, wenn der Block, in dem sie definiert ist, verlassen wird?
Wie unterscheiden sich formale Parameter von lokalen Variablen in Bezug auf ihre Lebensdauer?
Wie unterscheiden sich formale Parameter von lokalen Variablen in Bezug auf ihre Lebensdauer?
Was ist der Mechanismus, durch den formale Parameter ihren Wert erhalten?
Was ist der Mechanismus, durch den formale Parameter ihren Wert erhalten?
Welche Speicherklasse wird verwendet, um Variablen zu definieren, die im gesamten Programm ansprechbar sind, aber mit lokalen Variablen gleichen Namens verdeckt werden können?
Welche Speicherklasse wird verwendet, um Variablen zu definieren, die im gesamten Programm ansprechbar sind, aber mit lokalen Variablen gleichen Namens verdeckt werden können?
Welche Aussage trifft auf die Speicherklasse static
bei globalen Variablen zu?
Welche Aussage trifft auf die Speicherklasse static
bei globalen Variablen zu?
Was bewirkt die Deklaration einer lokalen Variable als static
?
Was bewirkt die Deklaration einer lokalen Variable als static
?
Welchen Zweck hat die Speicherklasse register
?
Welchen Zweck hat die Speicherklasse register
?
Was ist der Vorteil der Verwendung des const
-Attributs bei Variablen?
Was ist der Vorteil der Verwendung des const
-Attributs bei Variablen?
Warum sollte man const
-Deklarationen #define
-Direktiven vorziehen?
Warum sollte man const
-Deklarationen #define
-Direktiven vorziehen?
Welchen Zweck hat das volatile
-Attribut bei Variablen?
Welchen Zweck hat das volatile
-Attribut bei Variablen?
In welchem Szenario wäre die Verwendung des volatile
-Attributs besonders sinnvoll?
In welchem Szenario wäre die Verwendung des volatile
-Attributs besonders sinnvoll?
Welche der folgenden Aussagen beschreibt am besten die Ziele des C-Präprozessors?
Welche der folgenden Aussagen beschreibt am besten die Ziele des C-Präprozessors?
Welche der folgenden Aufgaben wird typischerweise vom C-Präprozessor NICHT ausgeführt?
Welche der folgenden Aufgaben wird typischerweise vom C-Präprozessor NICHT ausgeführt?
Wie fügt man den Inhalt einer Header-Datei in eine C-Quelldatei ein?
Wie fügt man den Inhalt einer Header-Datei in eine C-Quelldatei ein?
Was passiert, wenn eine #include-Direktive in C-Code auf eine Datei mit spitzen Klammern (z.B. #include <stdio.h>
) verweist?
Was passiert, wenn eine #include-Direktive in C-Code auf eine Datei mit spitzen Klammern (z.B. #include <stdio.h>
) verweist?
Wozu dienen #include-Anweisungen hauptsächlich?
Wozu dienen #include-Anweisungen hauptsächlich?
Was ist der Zweck von Makros, die mit der #define
-Direktive definiert werden?
Was ist der Zweck von Makros, die mit der #define
-Direktive definiert werden?
Wie werden symbolische Namen in der Regel in C-Programmen geschrieben?
Wie werden symbolische Namen in der Regel in C-Programmen geschrieben?
Was bewirkt die #undef
-Direktive?
Was bewirkt die #undef
-Direktive?
Welche Aufgabe hat der Präprozessor in Bezug auf bedingte Kompilierung?
Welche Aufgabe hat der Präprozessor in Bezug auf bedingte Kompilierung?
Was ist Modularisierung in der Programmierung?
Was ist Modularisierung in der Programmierung?
Welche Dateien werden typischerweise verwendet, um Schnittstellen in C zu definieren?
Welche Dateien werden typischerweise verwendet, um Schnittstellen in C zu definieren?
Was ist der Hauptunterschied zwischen einer Deklaration und einer Definition in C?
Was ist der Hauptunterschied zwischen einer Deklaration und einer Definition in C?
Welche Art von Informationen sollte ein Projektkommentar am Beginn einer Datei enthalten?
Welche Art von Informationen sollte ein Projektkommentar am Beginn einer Datei enthalten?
Wo sollte die Beschreibung externer Funktionen in Bezug auf Kommentare platziert werden?
Wo sollte die Beschreibung externer Funktionen in Bezug auf Kommentare platziert werden?
Was ist ein Zeiger in C?
Was ist ein Zeiger in C?
Was ist der Unterschied zwischen dem Inhaltsoperator *
und dem Adressoperator &
bezogen auf Zeiger?
Was ist der Unterschied zwischen dem Inhaltsoperator *
und dem Adressoperator &
bezogen auf Zeiger?
Wie definierst du einen Zeiger auf eine Variable vom Typ int
?
Wie definierst du einen Zeiger auf eine Variable vom Typ int
?
Was bedeutet es, wenn ein Zeiger 'nicht initialisiert' ist?
Was bedeutet es, wenn ein Zeiger 'nicht initialisiert' ist?
Welche der folgenden Benennungen deutet üblicherweise auf einen Zeiger hin?
Welche der folgenden Benennungen deutet üblicherweise auf einen Zeiger hin?
Wie kann ein Zeiger initialisiert werden, wenn er noch nicht auf eine bestimmte Variable zeigen soll?
Wie kann ein Zeiger initialisiert werden, wenn er noch nicht auf eine bestimmte Variable zeigen soll?
Was ist ein 'wilder Zeiger'?
Was ist ein 'wilder Zeiger'?
Was versteht man unter einem 'hängenden Zeiger' (dangling pointer)?
Was versteht man unter einem 'hängenden Zeiger' (dangling pointer)?
Was bedeutet 'Zeigerarithmetik'?
Was bedeutet 'Zeigerarithmetik'?
Welche Operation ist nicht möglich?
Welche Operation ist nicht möglich?
Flashcards
Funktionsaufruf
Funktionsaufruf
Ein Aufruf einer Funktion von einer anderen oder von sich selbst.
Call-by-Value
Call-by-Value
Die Übergabe von Argumentwerten wird kopiert und an die Funktion übergeben; Änderungen der Kopie beeinflussen die Umgebung nicht.
Call-by-Reference (Simulation)
Call-by-Reference (Simulation)
Simulation von Call-by-Reference durch die Übergabe von Zeigern.
Funktionsprototyp
Funktionsprototyp
Eine Deklaration, die die Signatur einer Funktion angibt und dem Compiler mitteilt, dass die Funktion existiert.
Signup and view all the flashcards
Funktionskommentare
Funktionskommentare
Beschreiben, was eine Funktion macht, nicht wie.
Signup and view all the flashcards
Implementierungskommentare
Implementierungskommentare
Beschreiben auf abstrakter Ebene, wie etwas gemacht wird.
Signup and view all the flashcards
Funktionen
Funktionen
Werden im Hautprogramm außerhalb aller Blöcke definiert und sind im gesamten Programm ansprechbar.
Signup and view all the flashcards
Globale Variablen
Globale Variablen
Werden im Hauptprogramm außerhalb aller Blöcke definiert und sind im gesamten Programm ansprechbar, können jedoch mit lokalen Variablen desselben Namens verdeckt werden.
Signup and view all the flashcards
Lokale Variablen
Lokale Variablen
Werden in einem Block definiert und sind nur innerhalb der Funktion / des Blocks ansprechbar und können durch Variablen in inneren Blöcken überdeckt werden.
Signup and view all the flashcards
Formale Parameter
Formale Parameter
Werden in einem Funktionskopf definiert. Sie leben ab ihrer Definition bis zum Ende der Funktion und sind nur innerhalb der Funktion ansprechbar.
Signup and view all the flashcards
Speicherklassen
Speicherklassen
extern: ...externe Variablen und ...externe Funktionen, static: ...statische Funktionen, ...statische globale Variablen, ...statische lokale Variablen
Signup and view all the flashcards
Globale Variable
Globale Variable
...darf es insgesamt nur eine Definition geben.
Signup and view all the flashcards
Funktion
Funktion
...darf es insgesamt nur eine Definition geben.
Signup and view all the flashcards
Statische Funktion
Statische Funktion
Die Sichtbarkeit wird auf den Rest der Quelldatei begrenzt, in der sich die Definition befindet.
Signup and view all the flashcards
Statische globale Variable
Statische globale Variable
Die Sichtbarkeit wird auf die Quelldatei begrenzt, in der sich die Definition befindet.
Signup and view all the flashcards
Statische lokale Variable
Statische lokale Variable
Ihr Speicherplatz und ihr Wert bleiben über den Funktionsaufruf erhalten.
Signup and view all the flashcards
Register-Variable
Register-Variable
Man kann dem Compiler mitteilen, dass eine Variable in einem Register gehalten werden soll
Signup and view all the flashcards
const
const
Variablen können nur einmal initialisiert und anschließend nicht mehr geändert werden können.
Signup and view all the flashcards
volatile
volatile
Der Wert einer Variablen wird bei jedem Zugriff neu aus dem Hauptspeicher gelesen.
Signup and view all the flashcards
Ziele des Präprozessors
Ziele des Präprozessors
Erhöhung der Lesbarkeit und Wartbarkeit von C-Programmen. Modularisierung von C-Programmen. Parallele Entwicklung von Software-Systemen im Team.
Signup and view all the flashcards
#include-Direktive
#include-Direktive
Der gesamte Inhalt einer Datei wird in den Quellcode eingefügt.
Signup and view all the flashcards
#define-Direktive
#define-Direktive
Es werden symbolische Namen definiert, die ab der Definition bis zum Ende der Datei durch den Ersatztext ersetzt werden.
Signup and view all the flashcards
Zusammenfassendes über den Präprozessor
Zusammenfassendes über den Präprozessor
Der Präprozessor überführt eine Quelldatei in ein Modul mit reinem C-Code.
Signup and view all the flashcards
Modularisierung von C-Programmen
Modularisierung von C-Programmen
Aufteilung eines Programms in mehrere Dateien und Auslagerung von Schnittstellen in Definitionsdateien.
Signup and view all the flashcards
Externe Variable
Externe Variable
Soll eine globale Variable verwendet werden, bevor sie definiert wird, muss sie als extern deklariert werden.
Signup and view all the flashcards
Externe Funktionen
Externe Funktionen
Soll eine extern deklarierte Funktion verwendet werden, die in einer anderen Quelldatei definiert ist, muss sie als extern deklariert werden.
Signup and view all the flashcards
Deklarationen
Deklarationen
Deklarationen machen den Namen und Typ bekannt.
Signup and view all the flashcards
Definitionen
Definitionen
Definitionen definieren und reservieren den entsprechenden Speicherplatz.
Signup and view all the flashcards
Zeiger
Zeiger
Variablen, deren Wert eine Speicheradresse ist.
Signup and view all the flashcards
Normale Variablen
Normale Variablen
Bei ihrer Definition ein Speicherbereich reserviert, in dem der Wert der Variablen abgelegt wird.
Signup and view all the flashcards
Zeigervariablen
Zeigervariablen
Bei ihrer Definition ein Speicherbereich reserviert, in dem die Adresse auf eine andere Speicherstelle abgelegt wird, an der der Wert abgelegt wird.
Signup and view all the flashcards
Zugriff auf die Variable
Zugriff auf die Variable
Liefert bei Zugriff auf die Variable den in diesem Speicherbereich abgelegte Wert.
Signup and view all the flashcards
Inhalts- oder Verweisoperator
Signup and view all the flashcards
&
&
Adressoperator
Signup and view all the flashcards
Zeigervariable
Zeigervariable
Zeigervariable muss vor ihrer ersten Verwendung definiert werden, ein definierter Zeiger, der noch nicht initialisiert wurde, zeigt auf eine zufällige Adresse.
Signup and view all the flashcards
Konstanten
Konstanten
Mit der const-Deklaration kann ein Zeiger oder der über ihn referenzierte Wert als konstant deklariert werden.
Signup and view all the flashcards
Hängende Zeiger
Hängende Zeiger
Zeiger, die auf alten, aber freigegebenen Speicherplatz verweisen, werden als „hängende Zeiger“ bezeichnet.
Signup and view all the flashcards
Wilde Zeiger
Wilde Zeiger
Zeigervariablen, die nicht initialisiert sind, nennt man „wilde Zeiger“, da sie auf einen beliebigen Speicherplatz verweisen.
Signup and view all the flashcards
Zeiger in Funktionen
Zeiger in Funktionen
Die Übergabe von Adressen an Funktionen. Die Idee ist, der Funktion die Möglichkeit zu geben, Variablen außerhalb ihres eigenen Gültigkeitsbereichs zu manipulieren.
Signup and view all the flashcards
Zeigerarithmetik
Zeigerarithmetik
Rechnen mit Zeigern der gleichen Datentyp.
Signup and view all the flashcards
Zeiger und Vektoren
Zeiger und Vektoren
Jeder Ausdruck mit Vektorindizes kann auch mit Zeigern formuliert werden. p_a + 1 zeigt auf das nachfolgende Vektorelement / p_a - 1 zeigt auf das vorausgehende Vektorelement.
Signup and view all the flashcards
Zeiger auf Zeiger
Zeiger auf Zeiger
Da Zeiger selbst Variablen sind, kann man – genau wie bei anderen Variablen – Zeiger auf sie definieren.
Signup and view all the flashcards
Void-Zeiger
Void-Zeiger
Ein Zeiger auf den Datentyp void. Einem void-Zeiger kann eine beliebige Adresse zugewiesen werden. Erst bei Zugriff auf den Wert muss der Datentyp bekannt sein.
Signup and view all the flashcards
Funktionszeiger
Funktionszeiger
Zeiger können auch auf Funktionen zeigen. Auch Funktionen belegen Speicherplatz ab einer bestimmten Adresse. Funktionen können über Zeiger aufgerufen werden.
Signup and view all the flashcards
Problemstellung Garbage Collection
Problemstellung Garbage Collection
Das Laufzeitsystem sucht bei einer Speicheranforderung nach der besten passenden Lücke, um die Anzahl der Lücken, d.h. die Fragmentierung des Speichers, einzuschränken.
Signup and view all the flashcards
Warum hat C keine automatische Garbage Collection?
Warum hat C keine automatische Garbage Collection?
Da es es nicht möglich, zu erkennen, welche Speicherbereiche vom Programm nicht mehr referenziert werden können. …und demzufolge freigegeben werden können.
Signup and view all the flashcards
Dynamische Vektoren
Dynamische Vektoren
Zeiger und dynamische Speicherverwaltung zusammen ermöglichen die Verwaltung dynamischer Vektoren.
Signup and view all the flashcardsStudy Notes
Funktionsaufrufe
- Funktionsaufrufe haben die allgemeine Form
<Funktionsname>(<Argumentliste>)
. - Beispiele für Funktionsaufrufe sind
exponent = power(2, n);
,power(2, n);
undprintf("%d %d\n", ++n, power(2, n));
. - Funktionen können innerhalb anderer Funktionen aufgerufen werden.
- Funktionen können sich selbst (Rekursion) oder gegenseitig aufrufen.
- Die Funktion
main
wird automatisch von der Laufzeitumgebung aufgerufen. - Das Ergebnis eines Funktionsaufrufs kann ignoriert werden.
Parameterübergabe
- In C erfolgt die Parameterübergabe immer als
call-by-value
. - Die Argumentwerte werden kopiert, und diese Kopie wird an die aufgerufene Funktion übergeben.
- Änderungen an der Kopie innerhalb der Funktion haben keine Auswirkungen auf die Umgebung außerhalb der Funktion.
call-by-reference
kann in C durch Übergabe von Speicheradressen (Zeigern) simuliert werden.- Zum Beispiel durch die Übergabe der Startadresse eines Vektors.
- Die Adresse selbst wird jedoch wieder als
call-by-value
übergeben. - Beim Funktionsaufruf werden die Argumente, falls nötig, implizit in den Typ der Parameter umgewandelt.
- Beim Verlassen der Funktion wird das Ergebnis im
return
-Statement implizit in den Ergebnistyp der Funktion umgewandelt, falls nötig.
Funktionsprototypen
- In C muss jeder Bezeichner vor seiner ersten Verwendung deklariert sein; dies gilt auch für Funktionen.
- Es ist nicht immer möglich, eine Funktion vor ihrer ersten Verwendung zu vereinbaren, zum Beispiel bei gegenseitiger Rekursion oder wenn eine Funktion in einer anderen Datei definiert ist.
- Funktionen können durch Funktionsprototypen deklariert werden.
- Funktionsprototypen geben die Signatur der Funktion an und werden mit einem Semikolon (
;
) abgeschlossen. - Ein Funktionsprototyp teilt dem Compiler mit, dass es die Funktion geben wird.
Kommentare in Funktionen
- Funktionskommentare beschreiben, was die Funktion macht, nicht wie es gemacht wird, und befinden sich beim Funktionsprototyp.
- Implementierungskommentare beschreiben auf abstrakter Ebene, wie etwas gemacht wird, und befinden sich innerhalb der Funktionsdefinition.
Lebensdauer und Sichtbarkeit (1)
- Funktionen werden im Hauptprogramm außerhalb aller Blöcke definiert.
- Lebensdauer: Funktionen "leben" ab ihrer Definition bis zum Ende des Programms.
- Sichtbarkeit: Funktionen sind im gesamten Programm ansprechbar.
- Globale Variablen bieten eine Alternative zum Austausch von Daten über Funktionsargumente und Funktionsergebnisse.
- Globale Variablen werden im Hauptprogramm außerhalb aller Blöcke definiert und bei Definition automatisch mit 0 initialisiert.
- Lebensdauer: Globale Variablen "leben" ab ihrer Definition bis zum Ende des Programms.
- Sichtbarkeit: Globale Variablen sind im gesamten Programm ansprechbar, können aber durch Variablen mit gleichem Namen in lokalen Blöcken verdeckt werden.
Lebensdauer und Sichtbarkeit (2)
- Lokale Variablen werden in einem Block definiert und mit dem Schlüsselwort
auto
eingeleitet, das üblicherweise weggelassen werden kann. - Bei der Definition wird Speicherplatz zugewiesen, der beim Schließen des Blocks automatisch wieder freigegeben wird.
- Daraus resultiert, dass lokale Variablen auch Auto-Variablen oder automatische Variablen genannt werden.
- Auto-Variablen werden bei ihrer Definition nicht initialisiert, ihr Wert ist zufällig.
- Lebensdauer: Sie "leben" ab ihrer Definition bis zum Ende des Blocks.
- Sichtbarkeit: Sie sind nur innerhalb der Funktion / des Blocks ansprechbar und können durch Variablen in inneren Blöcken überdeckt werden.
Lebensdauer und Sichtbarkeit (3)
- Formale Parameter werden im Funktionskopf definiert:
int my_param
- Bei Aufruf einer Funktion wird dem formalen Parameter Speicherplatz zugewiesen und dieser beim Verlassen der Funktion automatisch wieder freigegeben.
- Formale Parameter erhalten ihren Wert, indem der Wert des aktuellen Parameters in ihren Speicherplatz kopiert wird, d.h.,
call-by-value
. - Lebensdauer: Sie "leben" ab ihrer Definition bis zum Ende der Funktion.
- Sichtbarkeit: Sie sind nur innerhalb der Funktion ansprechbar und können durch Variablen in inneren Blöcken überdeckt werden.
Speicherklassen
- Variablen und Funktionen können verschiedenen Speicherklassen zugeordnet werden:
extern
- Externe Variablen
- Externe Funktionen
static
- Statische Funktionen
- Statische globale Variablen
- Statische lokale Variablen
register
- Register-Variablen
auto
ist ebenfalls eine Speicherklasse.
Externe Variable
- Globale Variablen, die verwendet werden, bevor sie definiert wurden, oder globale Variablen die in einer anderen Quelldatei definiert sind, müssen als
extern
deklariert werden - Eine
extern
-Deklaration reserviert keinen Speicherplatz und ermöglicht keine Initialisierung. - Vektorgrößen können weggelassen werden.
- Eine
extern
-Deklaration stellt sicher, dass dem Compiler Name und Typ einer globalen Variablen bekannt sind. - Für eine globale Variable darf es insgesamt nur eine Definition, aber mehrere Deklarationen geben.
Externe Funktionen
- Funktionen, die in einer anderen Quelldatei definiert sind, müssen als
extern
deklariert werden, wenn sie verwendet werden sollen. - Das Schlüsselwort
extern
wird bei Funktionsprototypen automatisch angenommen. - Für eine Funktion darf es insgesamt nur eine Definition, aber mehrere
extern
-Deklarationen geben.
Statische Funktionen
- Funktionen, die als
static
vereinbart wurden, sind nur innerhalb der Quelldatei sichtbar, in der sie definiert sind.static int ulam(int n);
static void get_sub_matrix(matrix[][MAX_SIZE], submatrix[][MAX_SIZE]);
- Statische Funktionen können als „private“ Funktionen betrachtet werden.
Statische globale Variablen
- Werden globale Variablen als
static
vereinbart, ist die Sichtbarkeit auf die Quelldatei, in der sie definiert wurden, beschränkt.static int position;
static int buffer[BUFSIZE];
- Variablen können gemeinsam von verschiedenen Funktionen innerhalb einer Datei verwendet werden aber sind nicht extern verfügbar.
- Statische Variablen werden bei Definition automatisch mit 0 initialisiert.
Statische lokale Variablen
- Wird eine lokale Variable als
static
vereinbart, behält sie ihren Speicherplatz und Wert über den Funktionsaufruf hinweg. - Die Variable wird nicht bei jedem Aufruf der Funktion neu erzeugt.
- Statische lokale Funktionen sind nur in der Funktion sichtbar, in der sie definiert sind.
- Die Lebensdauer beginnt mit der Definition bei dem ersten Aufruf der Funktion und endet erst mit der gesamten Programmausführung.
Register-Variablen
- Mittels register kann man dem Compiler mitteilen, dass eine Variable in einem Register gehalten werden soll.
register
kann bei lokalen Variablen und formalen Parametern einer Funktion angewendet werden.
void function(register unsigned int m)
{
register int i;
}
- Registervariablen werden nicht automatisch initialisiert.
- Deklaration nur bei häufig verwendeten Variablen sinnvoll.
- Der Compiler darf den
register
Hinweis ignorieren. - Compiler ignorieren fehlerhafte Deklarationen.
Konstanten
- Mittels
const
können Variablen nur einmal initialisiert und anschließend nicht mehr geändert werden können.const double PI = 3.141592654;
- Eine
const
-Vereinbarung ist für jeden Datentyp möglich. - Bei einem Vektor bedeutet dies, dass die Elemente des Vektors nicht geändert werden können:
const char PRIMES[] = {2, 3, 5, 7, 11, 13};
const
-Deklarationen sind#define
-Direktiven vorzuziehen, da sie Gültigkeitsbereiche und Typisierungen berücksichtigen.- Eine
const
-Vereinbarung ist zudem ein Optimierungshinweis für den Compiler. - Wird ein Parameter einer Funktion als
const
deklariert, kann innerhalb der Funktion keine Änderung der entsprechenden Variablen erfolgen.
Volatile
- Mittels
volatile
kann festgelegt werden, dass der Wert einer Variablen bei jedem Zugriff neu aus dem Hauptspeicher gelesen wird. - Sinnvoll eingesetzt werden kann dies in der Treiberprogrammierung, wenn Werte aus Geräteregistern gelesen werden oder wenn sich Werte außerhalb des Programmkontextes ändern.
- Optimierende Compiler würden beispielsweise eine Schleife als überflüssig ansehen und wegoptimieren, obwohl das Ergebnis der Schleife durch Änderungen in Geräteregistern maßgeblich beeinflusst werden würde.
Ziele des Präprozessors
- Erhöhung der Lesbarkeit und Wartbarkeit von C-Programmen
- Durch die Definition symbolischer Konstanten oder Makros
- Modularisierung von C-Programmen
- Funktionen, die inhaltlich nicht zusammen gehören, können auf verschiedene Dateien (Module) verteilt werden
- Parallele Entwicklung von Software-Systemen im Team
- Schnittstellendefinitionen (Funktionsprototypen, Typdefinitionen und Konstanten) können in Definitionsdateien ausgelagert werden und dann vom gesamten Team während der Entwicklung genutzt werden.
Einfügen von Dateien (1)
- Mit der
#include
-Direktive kann der gesamte Inhalt einer Datei in den Quellcode eingefügt werden. - Die Zeile mit der
#include
-Anweisung wird textuell durch den Inhalt der Datei ersetzt. - Der Dateiname wird (üblicherweise) ohne Pfad angegeben.
#include <Dateiname>
: Die Suche beginnt im Verzeichnis für Standard-Bibliotheken. Im Allgemeinen ist dieses über eine Umgebungsvariable definiert.#include "Dateiname"
: Die Suche nach der Datei beginnt in dem Verzeichnis, in dem sich auch die Quelldatei befindet. Wird sie dort nicht gefunden, geht die Suche wie bei<Dateiname>
weiter.
Einfügen von Dateien (2)
- Die eingefügte Datei darf weitere #include-Anweisungen enthalten, die auch ersetzt werden.
- Es gibt keinen Abbruch bei rekursivem
include
. #include
-Anweisungen stehen häufig am Anfang von Dateien, um- Bibliotheksfunktionen bekannt zu machen (beispielsweise
#include <stdio.h>
). - eigene Definitionsdateien, das heißt, Deklarationen von gemeinsam verwendeten Variablen und Funktionen, einzufügen.
- Bibliotheksfunktionen bekannt zu machen (beispielsweise
- Dadurch werden Programme gut strukturiert und Code-Redundanz vermieden.
Makros: Symbolische Namen
- Mit der #define-Direktive können symbolische Namen definiert werden:
#define <Name> <Ersatztext>
- Alle Vorkommen des symbolischen Namens werden ab der Definition bis zum Ende der Datei durch den Ersatztext ersetzt.
- Konvention: Namen in Großbuchstaben
#define YES ja
- Es werden nur Token ersetzt.
- Keine Ersetzung in
printf("YES")
oder inYESterday = today - 1
; - Mit der
#undef
-Direktive können symbolische Namen wieder gelöscht werden. - Mit der
#if
-Direktive (und#elif
,#else
,#endif
) kann die Ausführung des Präprozessors kontrolliert werden. Programmtexte können Quellcode und weitere Präprozessordirektiven enthalten.
Zusammenfassendes über den Präprozessor
- Der Präprozessor überführt eine Quelldatei in ein Modul mit reinem C-Code.
- Präprozessor-Anweisungen beginnen mit
#
und enden mit dem Zeilenumbruch. - Parametrisierte Makros können wie kleine Funktionen verwendet werden.
- Sorgfältige Klammerung, mehrfache Bewertung von Parametern. Makros haben gegenüber Funktionen sowohl Vorteile als auch Nachteile
- Bedingte Übersetzung
- Ermöglicht die Aufteilung von C-Programmen in mehrere Dateien (Verhinderung mehrfacher Includes)
- Ist sinnvoll, wenn ein Projekt für unterschiedliche Ziele übersetzt werden soll, beispielsweise für verschiedene Plattformen oder zur Unterscheidung von Debug- oder Release-Modus.
- Header-Dateien definieren die Schnittstelle eines C-Programms.
#include
-Direktiven fügen die gesamte Datei ein.- Ein Include der Standard Header-Dateien importiert nur die Namen. Die Bibliothek wird zu einem späteren Zeitpunkt vollständig zum Programm hinzugelinkt.
Modularisierung
- Es gibt in C kein explizites Modulkonzept.
- Eine Strukturierung von C-Programmen ist trotzdem möglich:
- durch Aufteilung eines Programms in mehrere Dateien
- Auslagerung von Schnittstellen in Definitionsdateien: Headerdateien (Suffix .h)
- Schnittstelle meint: Deklarationen von extern verwendeten
- symbolischen Konstanten
- Variablen
- Funktionen
Zur Erinnerung...
- Externe Variablen:
- Globale Variablen, die verwendet werden, bevor sie definiert wurden, oder globale Variablen, die in einer anderen Quelldatei definiert wurden, müssen als extern deklariert werden.
- Es darf nur eine Definition geben, es kann aber mehrere Deklarationen geben.
- Externe Funktionen:
- Funktionen, die in einer anderen Quelldatei definiert sind, müssen als
extern
deklariert werden, wenn sie verwendet werden sollen. - Es darf nur eine Definition geben, es kann aber mehrere Deklarationen geben.
- Funktionen, die in einer anderen Quelldatei definiert sind, müssen als
- Deklarationen machen den Namen und Typ bekannt.
- Definitionen definieren und reservieren den entsprechenden Speicherplatz.
Kommentare
- Projektkommentar zu Beginn der Datei
mainpage.h
odermain.c
/**
* @mainpage RSA-Verschlüsselung
*
* Dieses Projekt realisiert die RSA-Verschlüsselung ...
*
* @author Elfie Fast
* @date 2012-08-24
*/
- Dateikommentare zu Beginn der Datei
main.c
und der Header-Dateien
/**
* @file
* In diesem Modul wird ...
*
* @author Dieter Hacker
* @date 2012-08-24
*/
- Funktionskommentare:
- Bei externen Funktionen gehört die Beschreibung, (soweit es sich nicht um Interna der Implementierung handelt) zum Prototyp der Funktion in die Header-Datei.
- Die Beschreibung externer Variablen gehört ebenfalls in die Header-Datei.
- Externe Variablen sollten jedoch vermieden werden.
Prinzipien (1)
- Zeiger sind Variablen, deren Wert eine Speicheradresse ist; sie sind Zeiger auf eine Speicherstelle.
- Im Gegensatz dazu wird für normale Variablen bei ihrer Definition ein Speicherbereich reserviert, in dem der Wert abgelegt wird. Beim Zugriff auf die Variable wird der in diesem Speicherbereich abgelegte Wert geliefert.
- Für Zeigervariablen wird bei ihrer Definition ein Speicherbereich reserviert, in dem die Adresse auf eine andere Speicherstelle abgelegt wird, an der der Wert abgelegt wird.
- Beim Zugriff auf die Zeigervariable kann direkt auf die Adresse oder indirekt auf den Wert zugegriffen werden.
Prinzipien (2)
- Der Speicher kann man sich als einen Vektor von Speicherzellen, die fortlaufend nummeriert sind, vorstellen.
- Die Speicherzellen können einzeln oder in zusammenhängenden Gruppen bearbeitet werden, wobei beispielsweise der Datentyp char ein Byte, der Datentyp int eine Gruppe von vier hintereinander liegenden Bytes benötigt.
- Ein Zeiger benötigt eine Bytegruppe, die so groß ist, dass jede gültige Speicheradresse dort abgelegt werden kann.
- Üblich: vier Byte...
- Zeiger zeigen immer auf den Anfang einer Bytegruppe.
Operatoren
- Im Zusammenhang mit Zeigern gibt es in C zwei unäre Operatoren:
- Inhalts- oder Verweisoperator
*
- Syntax:
*Zeigervariable
- Mit dem Inhaltsoperator kann auf den Wert zugegriffen werden, auf den der Zeiger verweist.
- Syntax:
- Adressoperator
&
- Syntax:
&Variable
- Mit dem Adressoperator kann auf die Adresse einer Variablen zugegriffen werden.
- Syntax:
- Inhalts- oder Verweisoperator
Zeigervariablen
- Zeigervariablen müssen vor ihrer ersten Verwendung definiert werden.
Typname *Bezeichner;
- Durch die Definition wird Speicherplatz für einen Zeiger angelegt.
- Ein definierter Zeiger, der noch nicht initialisiert wurde, zeigt auf eine zufällige Adresse.
- Die Typangabe bezieht sich auf den referenzierten Wert, nicht die Variable selbst; die ist ein Zeiger.
- Die Definition eines Zeigers ist als Muster zu verstehen.
- Der Wert des Ausdrucks *Bezeichner ist vom Typ Typname.
Typname *Bezeichner
undTypname* Bezeichner
sind semantisch gleich.- Wichtig ist, dass man weiß, welcher Datentyp sich hinter einer Adresse verbirgt.
- Der Zeiger zeigt nur auf die Startadresse der Bytegruppe.
- Es gibt keine Möglichkeit herauszufinden, wie viel Speicher an der Stelle, auf die der Zeiger zeigt, zusammengehört.
Benennung
Typname *Bezeichner;
undTypname* Bezeichner;
sind semantisch gleich. In der Literatur zu C findet sich beides.- Die Namen von Zeigervariablen sollten darauf hinweisen, dass sie Zeiger sind:
p...
oderp_…
oderptr_…
oder…_ptr
- Ergibt dann z.B.
*p_int
oder*ptr_char
oder*long_int_ptr ...
Initialisierung von Zeigervariablen
- Zeiger desselben Typs können einander zugewiesen werden
- beide Zeiger verweisen auf denselben Speicherbereich.
- Eine Zeigervariable kann bei der Definition initialisiert werden...
- entweder mit
NULL
(benötigt:#include <stdlib.h>
).NULL
ist nichts anderes als die Adresse 0 - oder mit einem Ausdruck, der einen Zeiger zurückliefert.
- entweder mit
Spezialfälle
- Wilde Zeiger:
- Zeigervariablen, die nicht initialisiert sind, zeigen auf keinen definierten Speicherbereich.
- Hängende Zeiger
- Zeiger, die auf einen alten, aber freigegebenen Speicherplatz verweisen, werden als "hängende Zeiger" (engl. dangling pointer) bezeichnet
- Konstanten
- ein Zeiger oder der über ihn referenzierte Wert als konstant deklariert
- jeder Versuch, den Zeiger oder den Wert zu verändern zu einem Kompilierfehler führt
Zeiger als Funktionsargument
- Die Übergabe von Adressen an Funktionen ermöglicht die Parameterübergabe
call-by-reference
. - Die Übergabe der Speicheradresse ist weiterhin
call-by-value
. - Die Adresse, die der Zeiger als Wert hat, wird in den Speicherbereich für die Parameter der Funktion kopiert.
- Funktionen können Zeiger als Rückgabewerte liefern.
Zeigerarithmetik
- Da Zeiger ganzzahlige Adressen enthalten, kann mit ihnen gerechnet werden.
- Zu einem Zeiger kann eine ganze Zahl addiert oder subtrahiert werden. Damit sind beliebige Speicherbereiche adressierbar.
- Bei der Addition und Subtraktion mit Zeigern spielt der Datentyp des Zeigers eine wesentliche Rolle.
- Eine Addition (oder Subtraktion) mit 1 bedeutet, dass so viele Bytes addiert (oder subtrahiert) werden wie der referenzierte Datentyp groß ist.
- Ist wirklich nur mit Vektoren sinnvoll.
Zeiger und Vektoren
- Die Zeigerarithmetik wird am häufigsten im Zusammenhang mit Vektoren eingesetzt.
- Jeder Ausdruck mit Vektorindizes kann auch mit Zeigern formuliert werden, z.B.
array[i]
kann immer als*(array + i)
geschrieben werden. - Der C-Compiler wandelt alle Vektorzugriffe mittels
[]
intern in die Zeigerdarstellung um, also z.B.array[i]
⇒*(array + i);
- Unabhängig vom zugrunde liegenden Datentyp des Vektors gilt:
p_a + 1
zeigt auf das nachfolgende Vektorelement.p_a - 1
zeigt auf das vorausgehende Vektorelement.
- Die Subtraktion von zwei Zeigern liefert die Anzahl der Elemente zwischen den Zeigern.
- Der Wert einer Variablen oder eines Ausdrucks vom Typ Vektor ist die Adresse des Anfangselements des Vektors, d.h. des Elements 0. Diese Adresse kann auch einem anderen Zeiger zugewiesen werden.
Zeiger auf Zeiger
- Da Zeiger selbst Variablen sind, kann man – genau wie bei anderen Variablen – Zeiger auf sie definieren.
- Zeiger können – genau wie andere Variablen – in Vektoren zusammengefasst werden.
- Die Startadresse eines Zeigervektors kann als Parameter an eine Funktion übergeben werden.
Kommandozeilenargumente
- Eine
main
Funktion hat Zugriff auf Parameter:
int main(int argc, char **argv)
{ ... }
argc
enthält die Anzahl der übergebenen Parameter (mindestens 1, da der Programmname immer übergeben wird).argv
ein Vektor vonchar
-Zeigern, also Zeichenketten.- Diese Zeichenketten enthalten die übergebenen Parameter als Strings
- Das erste Element enthält den Programmnamen, alle weiteren Argumente in der Reihenfolge, wie sie übergeben worden sind.
void-Zeiger
- Ein Zeiger auf den Datentyp
void
- ist ein typenloser Zeiger und wird wie folgt deklariert:
void *p;
- Einem
void
-Zeiger kann eine beliebige Adresse zugewiesen werden.
- ist ein typenloser Zeiger und wird wie folgt deklariert:
void
-Zeiger werden verwendet, wenn der Datentyp des Wertes noch nicht feststeht oder es egal ist, um welchen Typ es sich handelt.- Erst bei Zugriff auf den Wert muss der Datentyp bekannt sein.
void
-Zeiger können durch eine explizite Typumwandlung in jeden anderen beliebigen Zeigertyp umgewandelt werden.
Funktionszeiger
- Zeiger können auch auf Funktionen zeigen.
- Auch Funktionen belegen Speicherplatz ab einer bestimmten Adresse.
- Funktionen können über Zeiger aufgerufen werden.
- Beim Funktionsaufruf wird zur Anfangsadresse der Funktion im Speicher verzweigt.
- Funktionen können über Zeiger als Argumente an andere Funktionen übergeben werden.
- Auch für Funktionszeiger können eigene Typen definiert werden.
- Die Übergabe von Funktionen als Parameter an andere Funktionen ist sinnvoll, wenn
- man eine Funktion für verschiedene Typen (polymorphe Funktionen) realisieren möchte
- man eine übergeordnete Datenstruktur realisieren will und dabei vom Typ der Elemente abstrahieren möchte
Dynamische Speicherverwaltung
- Bezeichnet, dass Speicher erst zur Laufzeit angefordert wird, wenn bekannt ist, wie viel Speicher benötigt wird, wieder freigegeben und dessen Größe bei Bedarf angepasst wird.
- Die Anforderung eines Speicherblocks liefert die Anfangsadresse des Blocks als Rückgabewert.
- Funktionen zur dynamischen Speicherverwaltung sind in der Standard-Bibliothek
stdlib.h
vordefiniert:- Anfordern eines Speicherblocks (
malloc
,calloc
) - Freigeben eines Speicherblocks (
free
) - Vergrößern und Verkleinern eines Speicherblocks (
realloc
)
- Anfordern eines Speicherblocks (
Problemstellung Garbage Collection (1)
- Bei jeder Speicheranforderung ist ein zusammenhängender Speicherbereich in der gewünschten Größe notwendig.
- Nicht mehr genutzter Speicher muss wieder freigegeben werden.
- Das Laufzeitsystem sucht nach einer passenden Speicherlücke, die Anzahl der Speicherlücken und die Fragmentierung des Speichers zu reduzieren.
- Die benutzten Speicherlücken werden immer weiter aufgeteilt.
- Wenn der Speicher nach vielen
malloc
- undfree
-Aufrufen so zerstückelt ist, dass kein zusammenhängender Speicherbereich in der benötigten Größe mehr existiert, obwohl insgesamt noch genügend Platz ist, tritt ein Problem auf.
Problemstellung Garbage Collection (2)
- Wenn das Programm nicht mehr genutzten Speicher nicht freigibt, kommt es auf dem System zu Speicherlecks .
- Auf C gibt es keine automatische Speicherbereinigung:
- Der Garbage Collector entfernt alle Objekte, auf die vom Programm nicht mehr verwiesen wird.
- Der Garbage Collector stellt den Speicherplatz der Speicherverwaltung zur Wiederverwendung zurück.
- Der Garbage Collector hält die Speicherfragmentierung möglichst gering.
Problemstellung Garbage Collection (3)
- C hat keine automatische Garbage Collection:
- In C und C++ kann mit Speicheradressen gerechnet und jede beliebige Adresse zugewiesen werden.
- Damit können Zeigerprinzipiell auf jede Stelle im Speicher zeigen.
- Es ist nicht möglich, Speicherbereiche vom Programm zu erkennen, die nicht mehr referenziert werden und freigegeben werden können...
Dynamische Vektoren
- Zeiger und dynamische Speicherverwaltung zusammen ermöglichen die Verwaltung dynamischer Vektoren:
- Die dynamische Speicheranforderung liefert einen zusammenhängenden Speicherbereich.
- Dieser kann als Vektor verwendet werden.
- Funktionen können Zeiger als Rückgabewerte liefern, beispielsweise die Startadresse von dynamisch allokiertem Speicher.
- Beispiel: „Dynamische Matrizen“
Strukturen
- Syntax
struct <Strukturname>
{
<Liste von Variablendeklarationen>
};
- Die Definition einer Struktur beginnt mit dem Schlüsselwort
struct
. - Danach folgt der optionale Name der Struktur (das Etikett).
- Die Variablen der Struktur heißen Komponenten der Struktur.
- Die Definition wird mit einem Semikolon
;
abgeschlossen.
struct PUNKT
{
int x; /* x-Koordinate des Punkts */
int y; /* y-Koordinate des Punkts */
};
Strukturvariablen
- es können Variablen definiert werden, die Strukturen wie
PUNKT
als Typ haben. - Bei der Definition einer Auto-Variablen über einem Strukturtyp wird – wie bei anderen Variablen auch – Speicherplatz reserviert.
- Die Komponenten der Struktur werden dabei direkt hintereinander im Speicher abgelegt.
- Strukturtypen müssen nicht benannt werden
- es ist möglich, direkt bei Definition einer Variablen einen anonymen Strukturtyp zu definieren
- Anonyme Strukturen können nicht als Rückgabetyp von Funktionen definiert werden
- in C sind zwei Strukturtypen nur dann gleich, wenn sie denselben Namen haben
- Für lokale Variablen über Strukturtypen...
- erfolgt – wie üblich – keine automatische Initialisierung.
- Für globale und statische Variablen über Strukturtypen...
- erfolgt – wie üblich – eine automatische Initialisierung mit 0.
Zugriff auf die Komponenten einer Struktur
- Mit dem Auswahloperator
.
kann auf die Komponenten einer Struktur zugegriffen werden. - Der Punktoperator ist analog zu dem Auswahloperator in objektorientierten Programmiersprachen zu verstehen.
Kopiervorgang bei Strukturen
- Da beliebige Variablen-Deklarationen in einer Struktur erlaubt sind, können hier auch Zeiger definiert sein
- In diesem Fall wird nur die gespeicherte Adresse kopiert, nicht aber das referenzierte Objekt.
- Man spricht daher von einer "flachen Kopie": shallow copy.
- Das Pendant zur shallow copy ist die "tiefe Kopie": deep copy.
Vergleich von Strukturen
- Strukturen lassen sich nicht ohne weiteres vergleichen!
- Der Vergleich muss für alle zu vergleichenden Komponenten explizit programmiert werden.
- Zum Vergleich von Strukturen müssen daher eigene Funktionen geschrieben werden, in denen festgelegt werden kann, ob flach oder deep verglichen werden soll.
Speicherorganisation
- Strukturen nur indirekt vergleichen, weil:
- Die Komponenten einer Struktur werden direkt hintereinander im Hauptspeicher abgelegt.
- Es entstehen kleine Lücken, sogenannte Pad-Bytes, in denen zufällige Werte stehen.
- Bei einem direkten Vergleich könnte die Strukturen deshalb einen falschen Wert annehmen.
- Die Größe einer Struktur kann mit dem
sizeof
-Operator bestimmt werden. - Strukturelemente können sich durch das Alignment der Speicherorganisation unterscheiden.
Zeiger auf Strukturen
- Zeiger auf Strukturen können definiert und verwendet werden.
- Die Adresse von Strukturvariablen kann mit dem Adressoperator
&
bestimmt werden. - Der Operator
.
zur Auswahl von Strukturkomponenten bindet enger als der Inhaltsoperator*
. - Zeiger auf Strukturen verwendet, um unnötiges Kopieren großer Strukturen zu vermeiden.
- Zeiger auf Strukturen wird oft verwendet abkürzende Schreibweise für den Komponentenzugriff:
(*p_point).x
kann mitp_point->x
dargestellt werden
- Vor der Anwendung des Auswahloperators
->
muss sichergestellt sein, dass der Zeiger auch auf einen Speicherplatz verweist.
Parameter bei Prozeduren: Übergabe via Zeiger
- Vorteile, einen Zeiger auf eine Struktur an eine Funktionen zu übergeben: Wenn die Struktur in der Funktion verändert und die Änderungen in der aufrufenden Umgebung sichtbar sein müssen
- Übergabe mit
call-by-value
bei großen Strukturen aufwändig - Die Rückgabe von Zeigern auf Strukturen im Zusammenhang mit dynamischer Speicherallokation ist sinnvoll
Rekursive Strukturen
- Strukturen können wechselseitig referenziert werden.
- Die Referenzierung kann nur über Zeiger erfolgen
- Strukturelemente dürfen sich selbst nicht enthalten, aber auf sich selbst verweisen.
- Beispiel: Struktur für Elemente einer doppelt verketteten Liste
- Es ist sinnvoll, Strukturen über Zeiger zu referenzieren. Verwendung von Strukturvariablen in manchen Fällen angebracht.
- Beispiele könnten bei der verketteten Liste sinnvoll sein
- Die Liste über eine Strukturvariable...
- Die Einträge über Zeiger zu referenzieren
Wissenswertes über Strukturen
- Mit Strukturen können Daten, die inhaltlich zusammengehören, zusammengefasst werden. Sie können (fast) wie primitive Datentypen verwendet werden.
- Bei der Definition von Strukturvariablen wird Speicherbereich für die ganze Struktur mit allen Komponenten bereitgestellt.
- Bei Zuweisungen, der Übergabe an Funktionen als Argumente, oder der Rückgabe von Funktionen wird die ganze Struktur kopiert.
- Daher sollten große Strukturen über Zeiger referenziert werden.
- Strukturen sind als Elemente von Vektoren erlaubt.
Speicherorganisation
- Die Komponenten einer Struktur liegen nicht immer lückenlos im Speicher hintereinander.
- Strukturen können daher nicht mit == verglichen werden, und die Größe der Struktur kann nicht aus der Größe ihrer Komponenten berechnet werden.
- Strukturen können sich nicht selbst enthalten. Bei rekursiven Strukturen muss auf die Komponenten desselben Strukturtyps über Zeiger verwiesen werden.
- Der Zugriff auf die Komponenten erfolgt mit dem Operator
.
, bei Zugriffen über einen Zeiger steht der Pfeiloperator->
zur Verfügung.
Unionen
- Eine
Union
ist eine Struktur, bei der nur eine der Komponenten belegt werden kann. - Die Komponenten einer
Union
werden Alternativen genannt. - Eine Union benötigt den Speicherplatz der grössten Alternative.
- Unionen sparen gegenüber Strukturen Speicherplatz, wenn Komponenten einer Struktur nie zusammen auftreten.
- Unionen sind nur bedingt über verschiedene Systeme/Plattformen austauschbar sind und vom Alignment abhängig.
- Über Unionstypen können Variablen definiert werden:
- Einer Unionsvariablen kann ein Wert zugewiesen werden - aus Datentypen der enthaltenen Alternativen.
- Unionsvariablen können bei Definition initialisiert werden.
- Wird auf eine Union zugegriffen, muss man wissen, welcher Datentyp zuletzt gespeichert wurde.
- Unionen können in Strukturen und Vektoren vorkommen.
Bitfelder
- Bitfelder sind Strukturkomponenten, die mit weniger als einem Byte in eine Struktur gepackt werden können.
- Ein Bitfeld besteht aus einer bestimmten Anzahl an Bits.
- Die Länge wird vom Bitfeld-Namen durch einen Doppelpunkt getrennt.
unsigned int is_keyword : 1
- Ein Bitfeld kann Komponente einer Struktur oder Union sein.
- Da Bitfelder keine adressierbare Speicherstelle belegen, ist der Adressoperator
&
nicht anwendbar. - Bitfelder werden vor allem in der hardwarenahen Programmierung eingesetzt.
Variable Argumentlisten (Ellipsen)
- C erlaubt die Definition von Funktionen mit einer variablen Anzahl an Argumenten.
- Die Signatur solcher Funktionen enthält eine Ellipse in der formalen - Parameterliste:
- Eine Ellipse besteht aus drei Punkten: ...
- Nach dem letzten Parameter in der Parameterliste angegeben
- Die Funktion muss explizit einen Parameter haben.
- Variable Argumentlisten sind gefährlich:
- Der Compiler prüft die Argumente nicht.
- Es liegt an der Hand des Aufrufers, dass die Argumente korrekt sind.
- Es gibt folgende vier Makros und einen Datentyp in der Standardheaderdatei stdarg.h der Programmiersprache C:
- va_list - einen Datentyp zur Darstellung eines Zeigers auf ein Argument in einer Argumentliste
- va_start - ein Makro, das diesen Zeiger auf das erste Argument in der Liste setzt
- va_arg - ein Makro, das das aktuelle Argument liefert und dann den Zeiger auf das nächste Argument setzt
- va_copy - ein Makro, das einen Zeiger in einen anderen kopiert
- va_end - ein Makro, das Aufräumarbeiten vornimmt
Studying That Suits You
Use AI to generate personalized quizzes and flashcards to suit your learning preferences.