C Programmierung - Vorlesung 3 Vorgänge während der

Werbung
Vorgänge während der Übersetzung eines C Programms
C Programmierung - Vorlesung 3
Hochschule Regensburg
23.03.2012
Universitätsstraße 31, 93053 Regensburg
Bei jedem Aufruf des Compilers laufen folgende Schritte ab
1
Vorverarbeitung des Source-Files (pre-processing)
◮ folgt auf einen backslash-Character (ASCII 0x5c) ein
newline-Character (0xa), so werden beide Zeichen entfernt
◮ Preprocessor Direktiven (#Makros) werden ausgeführt
(Textersetzung bei define, include usw.)
◮ aufeinander folgende String-Literale werden konkateniert
2
Übersetzung der resultierenden Tokens in Maschinencode
3
Auflösung externer Referenzen durch den Linker
Prof. Dr. Jan Dünnweber
Prof. Dr. Jan Dünnweber, Folie 2 von 18
Scope: Der Geltungsbereich von Vereinbarungen
Programmierung 1
Beispiele für Vereinbarungen mit unterschiedlichem Scope
Bezeichner haben unterschiedliche Geltungsbereiche
Scope Type
File Scope
Block Scope
Prototype Scope
Ort der Deklaration
Außerhalt aller
Blöcke und
Parameterlisten
Zu Beginn eines
Blocks bzw. in einer
Funktionsdefinition
In einem
Funktionsprototyp
Geltungsbereich
Global, bis zum
Ende der Datei bzw.
Übersetzungseinheit
Lokal, bis zum
Ende des Blocks
bzw. der Funktion
nirgends (!), die
Bezeichner haben
Kommentar-Charakter
Anmerkung: Eine Ausnahme stellen Labels dar.
Diese haben Funktion Scope, d.h. Geltung in einer Funktion,
unabhängig von der Blocktiefe
Es gilt jedoch: Labels haben i.A. nur Kommentar-Charakter
(Verwendung im Zusammenhang mit //////
goto ist nicht weiter relevant)
Prof. Dr. Jan Dünnweber, Folie 3 von 18
Programmierung 1
Definition einer einfach verketteten Liste
struct Node { /∗ ... ∗/
struct Node ∗next;
};
void printNode(const struct Node ∗ptrNode);
int printList(const struct Node ∗first)
struct Node ∗ptr = first;
while (ptr != NULL) {
printNode(ptr);
ptr = ptr−>next;
}
}
{
Node, next, printNode & printList haben alle File Scope
ptrNode hat Prototype Scope
first & ptr haben Block Scope
Prof. Dr. Jan Dünnweber, Folie 4 von 18
Programmierung 1
Namensräume und Bezeichner
Vorzeichenbehaftete Typen und Synonyme
Jeder Bezeichner fällt in eine der folgenden Kategorien
Label mit Kommentar-Charakter
Tag zur Markierung von structure- und union-Typen
Member Name innerhalb von structure- und union-Typen
Gewöhnliche Bezeichner
Da jeder Kategorie ein Namensraum (namespace) zugeordnet ist, kann
ein Bezeichner konfliktfrei mehrere kontextabhängige Bedeutungen haben
Beispiel
struct pin { char pin[16]; /∗ ... ∗/ }
_Bool check_pin(struct pin ∗pin) {
int len = strlen(pin−>pin); }
Die primitiven Typen haben z. T. (mehrere) Synonyme
Typ
Synonym
signed char
int
signed, signed int
short
short int, signed short,
signed short int
long
long int, signed long,
signed long int
long long
long long int, signed long long,
signed long long int
pin referenziert hier sowohl den Typ (Tag struct pin) als auch
das zugehörige char-Array (Member char pin[]) und einen Zeiger
auf eine Variable (Member char pin[])
Mittels pin->pin wird die Member-Variable wieder dereferenziert
Prof. Dr. Jan Dünnweber, Folie 5 von 18
Programmierung 1
Vorzeichenlose Typen und Synonyme
Auch für vorzeichenlose Primitive gibt es z. T. (mehrere)
unteschiedliche Schreibweisen (Synonyme)
Typ
Synonym
unsigned char
char
unsigned int
int
unsigned short
unsigned short int
unsigned long
unsigned long int
unsigned long long unsigned long long int
Prof. Dr. Jan Dünnweber, Folie 7 von 18
Programmierung 1
Prof. Dr. Jan Dünnweber, Folie 6 von 18
Programmierung 1
Der Spezielle Typ void
void hat (kontextabhängig) unterschiedliche Bedeutungen
Kontext
Beispiel
Bedeutung
in einer
void exit(void)
Die Funktion
Funktionsdeklaration
Ausdruck
(void)exit();
vom Typ
void
Zeiger
auf
void
void *malloc(size t t);
void free(void *ptr);
Prof. Dr. Jan Dünnweber, Folie 8 von 18
hat kein Ergebnis
und keine Parameter
Der Ausdruck
hat keinen
Wert
Reservierung bzw.
Freigabe von Speicher
unbestimmten Typs
Programmierung 1
Array-Typen: Motivation
Verwendung von Arrays, Regeln
Addition von Zahlen
Es ist v. a. wichtig, auf korrekte Indizierung zu achten
#include <stdio.h>
int main(int argc, char∗ argv[]) {
int x,y,z,sum;
x = 1; y = 2; z = 4;
sum = x + y + z;
printf("summe = %d \n", sum); /∗ Summe ausgeben ∗/
return 0;
}
Felder (engl. Arrays) werden durch eckige Klammern hinter dem
Variablennamen deklariert
Auf die einzelnen Elemente des Feldes wird dann mit einem Index
zugegriffen
Beim Feld mit N Elementen erfolgt die Zählung von 0 bis N-1
Alternativ lassen sich die Werte in einem Array zusammen fassen
Addition der Feldelemente
#include <stdio.h>
int main(int argc, char∗ argv[]) {
int sum, feld[3];
feld[0] = 1; feld[1] = 2; feld[2] = 4;
sum = feld[0] + feld[1] + feld[2];
printf("summe = %d \n", sum); /∗ Summe ausgeben ∗/
return 0;
}
Prof. Dr. Jan Dünnweber, Folie 9 von 18
Programmierung 1
Auswertung der Kommandozeile
Auch bei der Kommandozeilenauswertung wird ein Array verwendet
Das Betriebsystem übergibt dem Programm zwei Werte
1 Die Anzahl der Argumente über den Formalparameter:
int argc
2 Ein Feld von Zeigern auf Zeichenketten via char *argv
Beispiel: Array mit 3 primitiven Elementen
int feld[3];
feld[0] = 1;
feld[1] = 2;
feld[2] = 3;
/////////////////
feld[3] = 4;/
Ein Array ist ein homogener Verbundtyp invariabler Größe
Prof. Dr. Jan Dünnweber, Folie 10 von 18
Programmierung 1
Ausgabe aller Parameter
Vereinbarte Parameter (z. B. in Prototypen) heißen auch
Formalparameter, die tatsächlich übergebenen Argumente nennt
man Aktualparameter (en. formal/actual)
Im folgenden sollen alle Argumente (Aktualparameter) in der
Konsole ausgegeben werden
Beispiel: Einfaches echo-Programm
Beispiel: Einfaches wc-Programm (word count)
#include <stdio.h>
int main(int argc, char∗ argv[]) {
printf("# Argumente = %d \n", argc);
return 0;
}
#include <stdio.h>
int main(int argc, char∗ argv[]) {
printf("# Argumente = %d \n", argc);
printf("0. Arg = %s \n", argv[0]);
printf("1. Arg = %s \n", argv[1]);
printf("2. Arg = %s \n", argv[2]);
return 0;
}
Was passiert bei weniger als 3 Argumenten?
Prof. Dr. Jan Dünnweber, Folie 11 von 18
Programmierung 1
Prof. Dr. Jan Dünnweber, Folie 12 von 18
Programmierung 1
Die while-Schleife
NullPointer
Parameterwert argv[0] enthält den Programmnamen
Die Parameter argv[1], ... , argv[argc-1] enthalten die
Argumente
Jeder Zugriff auf höherwertige Arrayindices führt zu Fehlern,
i. A. null-Pointer
Die while-Schleife (en. loop) ist eine der einfachsten
Kontrollstrukturen in C und dient der repetitiven Evaluation von
Ausdrücken (≈ wiederholt) z. B. zur iterativen Verarbeitung
(≈ schrittweise) von Verbundtypen wie Arrays
Die Syntax der while-Schleife sieht aus, wie folgt
Beispiel: Zugriff auf ein undefiniertes Array-Element
Formale Spezifikation der while-Schleife in C
./ausgabe bla
# Argumente = 2
0. Arg = ./ausgabe
1. Arg = bla
2. Arg = (null)
while( <expr> ) /∗ Kopf/Head(er) bzw. Definition
∗/
statement; /∗ Rumpf/Body bzw. abhaengige Anweisung(en) ∗/
Arraybezeichner (Identifier) sind nur Zeiger (Pointer) auf das erste
Element des Arrays, beim Zugriff mittels Index wird ein Vielfaches
der Elementgröße addiert. Dementsprechend sind Indizierungen über
Arraygrenzen hinaus zulässig (sogar negative Indices) aber nur
selten sinnvoll (⇒ i. A. zu vermeiden!)
Prof. Dr. Jan Dünnweber, Folie 13 von 18
Programmierung 1
Auswertung der while-Schleife
Wann ist <expr> wahr bzw. wann und ggf. wie lange wird der Body
der while-Schleife ausgeführt?
Sofern das statement im Body der while-Schleife aus mehreren
Anweisungen besteht, müssen diese von geschweiften Klammern
({}, d. h. als Block) umgeben sein
Aus Gründen der Lesbarkeit empfiehlt es sich, immer einen Block
für den Body zu verwenden
Prof. Dr. Jan Dünnweber, Folie 14 von 18
Programmierung 1
Parameterausgabe mittels while-Schleife
Der Body der while-Schleife wird so lange ausgeführt,
bis die Bedingung index < argc verletzt ist
Laufzeitbedingungen in der while-Schleife
Echo Programm mit while-Schleife
while( <expr> ) /∗solange <expr> == "true" ... ∗/
statement;
/∗ evaluiere body
∗/
int main(int argc, char ∗argv[]) {
int index;
index = 0;
printf("# Args %d \n",argc);
while(index < argc) {
printf("%d. Arg %s\n",index,argv[index]);
index = index + 1;
}
return 0;
}
In C gibt es keinen boolschen Datentyp für false und true
0 entspricht in C dem logischen Wert false, jeder beliebige andere
Wert entspricht true
⇒ Die Anweisung statement wird so lange ausgeführt, bis das Ergebnis
von <expr> gleich 0 ist
Prof. Dr. Jan Dünnweber, Folie 15 von 18
Programmierung 1
So wird ein unzulässiger Index vermieden
Prof. Dr. Jan Dünnweber, Folie 16 von 18
Programmierung 1
weiteres Beispiel: Berechnung des Mittelwerts
Was haben wir gelernt?
Der while-header <expr> enthält nicht immer einen Zähler
top-driven average computation
Zusammenfassung
/∗ Read in numbers from the keyboard and
∗ print out their average.
∗/
#include <stdio.h>
int main(int argc, char∗ argv[]) {
double x = 0.0, sum = 0.0; int count = 0;
printf( "\t−−− Calculate Averages −−−\n" );
printf( "\nEnter some numbers:\n"
"(Type a letter to end your input)\n" );
while ( scanf( "%lf", &x ) == 1 )
{
sum += x;
++count;
}
if ( count == 0 )
printf( "No input data!\n" );
else
printf( "The average is %.2f\n", sum/count );
return 0;
}
Bezeichner haben in C kontextabhängige Geltungsbereiche (Scope)
und Bedeutung (Semantik)
Bereits in einfachen Ausdrücken ist elemtare Pointer Arithmetik
unvermeidbar
Um Konflikte zu vermeiden, sollte man Namensäume und Typen
(inkl. möglicher Synonyme) gut kennen
Ein einfacher Verbundtyp ist das Array (≈ Feld)
Eine einfache Kontrollstruktur ist die while-Schleife
Die while-Schleife wird oft zusammen mit Arrays für Iterationen
benutzt
c
O’Reilly
“C In A Nutshell” (p. 85)
Prof. Dr. Jan Dünnweber, Folie 17 von 18
Programmierung 1
Prof. Dr. Jan Dünnweber, Folie 18 von 18
Programmierung 1
Zugehörige Unterlagen
Herunterladen