Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Prof. Dr. Margarita Esponda
Die C-Programmiersprache
Wiederholung (Teil 1)
WS 2011/2012
1
C
Assemblersprachen
Hochprogrammiersprachen
C-SpracheWeil C ursprünglich für die Programmierung des UNIX Betriebssystems entworfen worden ist.
Prof. Dr. Margarita Esponda
2
Eigenschaften von C
• Die Programme sind sehr kompakt
• Erlaubt Hardware nahe Programmierung
• In C lassen sich sehr effiziente Programme schreiben
• Es wird immer noch sehr breit verwendet
Prof. Dr. Margarita Esponda
3
Eigenschaften von C
gute
schlechte
Der Programmierer kann machen was er will.
Der Programmierer kann machen was er will.
Der C-Compiler geht davon aus, dass der Programmierer genau weiß, was er will, und meldet fast keine Warnungen beim Übersetzen des Programms. Die meisten Fehler bei C-Programmen treten erst zur Laufzeit auf.
Prof. Dr. Margarita Esponda
4
C ist eine formatfreie Sprache!
m(f,a,s)char*s;
{char c;return f&1?a!=*s++?m(f,a,s):s[11]:f&2?a!=*s++?1+m(f,a,s):1:f&4?a--?
putchar(*s),m(f,a,s):a:f&8?*s?m(8,32,(c=m(1,*s++,"Arjan Kenter. \no$../.\""),
m(4,m(2,*s++,"POCnWAUvBVxRsoqatKJurgXYyDQbzhLwkNjdMTGeIScHFmpliZEf"),&c),s)):
65:(m(8,34,"rgeQjPruaOnDaPeWrAaPnPrCnOrPaPnPjPrCaPrPnPrPaOrvaPndeOrAnOrPnOrP\
nOaPnPjPaOrPnPrPnPrPtPnPrAaPnBrnnsrnnBaPeOrCnPrOnCaPnOaPnPjPtPnAaPnPrPnPrCaPn\
BrAnxrAnVePrCnBjPrOnvrCnxrAnxrAnsrOnvjPrOnUrOnornnsrnnorOtCnCjPrCtPnCrnnirWtP\
nCjPrCaPnOtPrCnErAnOjPrOnvtPnnrCnNrnnRePjPrPtnrUnnrntPnbtPrAaPnCrnnOrPjPrRtPn\
CaPrWtCnKtPnOtPrBnCjPronCaPrVtPnOtOnAtnrxaPnCjPrqnnaPrtaOrsaPnCtPjPratPnnaPrA\
aPnAaPtPnnaPrvaPnnjPrKtPnWaOrWtOnnaPnWaPrCaPnntOjPrrtOnWanrOtPnCaPnBtCjPrYtOn\
UaOrPnVjPrwtnnxjPrMnBjPrTnUjP"),0);}
main(){return m(0,75,"mIWltouQJGsBniKYvTxODAfbUcFzSpMwNCHEgrdLaPkyVRjXeqZh");}
smile.c „The International Obfuscated C Code Contest“
Prof. Dr. Margarita Esponda
5
Grundlegende Konventionen
Formatierung
Dem Compiler ist die Formatierung des Programms egal, dem menschlichen Leser aber nicht!
- Kommentare
- nicht mehr als 79 Zeichen in eine Zeile schreiben,
- übliche Einrückungstiefe innerhalb eines Blockes: 3 Zeichen
int func( void ){ /* Anweisungen */ /* Anweisungen */ /* Anweisungen */}
Beispiel:
- die Grenzen eines Block deutlich machen,
Prof. Dr. Margarita Esponda
6
0110101110101011101100000110
0110101110101011101100000110
011001110010110110000110
0110101110101011101100000110
0110101110101011101100000110
0110101110101011101100000110
0110101110101011101100000110
0110101110101011101100000110
goto goto
goto
goto
goto
goto
goto
goto
goto
goto
„Spaghetti code“
Prof. Dr. Margarita Esponda
7
Erste einfache Grundstruktur
eines C-Programms
Direktiven für den Präprozessor
Typ main ( . . . )
{
}
Anweisungsfolge
Typ funktion_1 ( . . . )
{
}
Anweisungsfolge
Typ funktion_2 ( . . . )
{
}
Anweisungsfolge
Typ funktion_n ( . . . )
{
}
Anweisungsfolge
.
.
viele kleine Funktionen
nur eine kleine Main-Funktion
Prof. Dr. Margarita Esponda
8
Erste einfache Grundstruktur eines C-Programms
Direktiven für den Präprozessor
int main ( . . . )
{
}
Anweisungsfolge
Typ funktion_1 ( . . . )
{
}
Anweisungsfolge
Typ funktion_2 ( . . . )
{
}
Anweisungsfolge
Typ funktion_n ( . . . )
{
}
Anweisungsfolge
.
.
Typ funktion_1 ( . . . );
Typ funktion_2 ( . . . );
Typ funktion_n ( . . . );..
Funktionsprototyp
Direktiven für den Präprozessor
int main ( . . . )
{
}
Anweisungsfolge
Typ funktion_1 ( . . . )
{
}
Anweisungsfolge
Typ funktion_2 ( . . . )
{
}
Anweisungsfolge
Typ funktion_n ( . . . )
{
}
Anweisungsfolge
.
.
Prof. Dr. Margarita Esponda
9
Die vier Phasen des Übersetzens
"Source Code"
Quellcode
#include <stdio.h>
void main() { printf ( "Hi!" ); }
void main() { printf ( "Hi!" ); }
Die include-Anweisung wird durch den Inhalt der Datei "stdio.h" ersetzt.
Präprozessor
Compiler
. . .main proc near@0: push ebp mov ebp,esp@1: push offset s@ call _printf pop ecx xor eax,eax@3:@2: pop ebp ret _main endp. . .
Assembler
Linker0101010111110101
00001010101111111111110101010111
00101010101111110000000111101011
1111101010101011
00010110111010100101010101010100
Bibliotheken
0000011101101011
01010101111101010000101010111111
11111101010101110010101010111111
0000000111101011
11111010101010110001011011101010
01010101010101001010101010101010
" Assembly Code" " Object Code"
" Executable Code"
Ausführbarer Programm
name.outLinux
Prof. Dr. Margarita Esponda
10
C-Programme
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
QuellcodeDatei
Compiler Compiler Compiler Compiler
QuellcodeDatei
ObjektcodeDatei
QuellcodeDatei
ObjektcodeDatei
QuellcodeDatei
ObjektcodeDatei
QuellcodeDatei
ObjektcodeDatei
LinkerLaufzeit-
Bibliothek
AusführbaresProgramm
Prof. Dr. Margarita Esponda
11
Formatierte Ausgabeprintf( "formatstring", var1 , var2 , . . . , varn );
int x;
printf ( " %d", x );
int x, y, z;
printf( " %d \n %d \n %d ", x, y, z);
printf( " %d %d %d " , x, y, z );
Zuordnung zwischen Formatangaben und Variablen
einigeFormatangaben
%d als dezimale ganze Zahl ausgeben%f als Gleitkommazahl ausgeben
%6.2f mindestens 6 Zeichen breit und 2 Ziffern hinter dem Punkt%c als einzelnes Zeichen ausgeben
%u als dezimale ganze Zahl ohne Vorzeichen ausgeben%x als hexadezimale ganze Zahl ausgeben
%o als oktale ganze Zahl ausgeben%e als Gleitkommazahl in Exponent-Darstellung ausgeben
Prof. Dr. Margarita Esponda
12
Formatierte Ausgabe
!
#include <stdio.h>
void main() {
int zahl = -3;
printf( "Zahl = %i \n" , zahl );
printf( "Zahl = %u \n" , zahl );
printf( "Zahl = %x \n" , zahl );
printf( "Zahl = %o \n" , zahl );
printf( "Zahl = %f \n" , zahl );
printf( "Zahl = %e \n" , zahl );
}
Zahl = 0.000000
Zahl = -3
Zahl = 4294967293
Zahl = fffffffd
Zahl = 37777777775
Zahl = 1.678055e-307
Prof. Dr. Margarita Esponda
13
Formatierte Ausgabe
!
. . .
printf( "Zahl = %.324f \n" , zahl );
}
Zahl = 0.000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000
0000000000000000000004409019194593899
int zahl = -3
C legt für jeden elementaren Datentyp eine minimale Speichergröße
und damit einen minimalen Zahlenbereich fest.
Prof. Dr. Margarita Esponda
14
C99 Standard
"<inttypes.h>"
Für die printf()-Funktion gibt es einige neue Modifizierer für die Formatausgabe.
"j" Für intmax_t (e.g. "%jd") or uintmax_t (e.g. "%ju").
Prof. Dr. Margarita Esponda
15
Typ-Operatoren sind zu empfehlen
small = (int) big ;
Vorsicht mit Informationsverlust !!!
double big = 993939.067;
int small;
f = k/j ;
int j = 2, k = 3;
float f;
f = (float) k/j ;
int j = 2, k = 3;
float f;
f bekommt 1.0 f bekommt 1.5
small bekommt 993939
Operator Zeichen Rtg. Beispiel Priorität
cast-Operator (typ) (int) a 13
sizeof-Operator sizeof(expresion) sizeof( 6 )
Prof. Dr. Margarita Esponda
16
Type-Checking
C:
Vertraut dem Programmierer!
Wenn er Birnen mit Mangos vergleichen will:
Let it be!
Java:
Vertraut nur Programmen, die die statische Analyse des
Compilers zugelassen hat.
Python:
Vertraut weder dem Programmierer noch dem Compiler
und kontrolliert alles zur Laufzeit.
Prof. Dr. Margarita Esponda
17
Inkrement- und Dekrement-Operatoren
13Inkrementiert op um 1
op ++++
PrioritätBeschreibung BenutzungOperator
13++ op ++
13Dekrementiert op um 1
op ----
13-- op--
...int a = 5; int b = 4;int c = 0;int d = 0;c = --a + b++; c = c + ++a;d = c++ - ++a + b--;...
a? b? c? und d?
Was ist der Inhalt der Variablen
6 4 14 12
Prof. Dr. Margarita Esponda
18
Bit-Operatoren
Operator Zeichen Rtg. Beispiel Priorität
Bitkomplement ~ ~ a 13
unär
binär
Linksschieben << op1 << op2 10
Rechtsschieben >> op1 >> op2 10
Logische Bitoperatoren
bitweise UND & a & b 7
bitweise ex-ODER ^ a ^ b 6
bitweise ODER | a | b 5
Prof. Dr. Margarita Esponda
19
Beispiel:
6 & 17
6 | 7
-6 ^ 7
-6 ^ -6
0000011000010001
0
6 =17 =
00000000
0000011000000111
7
6 =7 =
00000111
1111101000000111
-6 =7 =
11111101 -3
11111010-6 =
00000000 011111010-6 =
Prof. Dr. Margarita Esponda
20
Formatierte Eingabe
scanf( "formatstring", adr1, adr2,...,adrn );
int x;
scanf ( "%d", &x );
int x, y, z;
scanf( "%d %d %d ", &x, &y, &z );
scanf( " %d %d %d ", &x, &y, &z );
Eingabewerte
3 5 7
Zuordnung zwischen Eingabewerten, Formatangaben und Zielvariablen
21
c:>
c:>
Beispiel:
#include <stdio.h>
printf( "Zahl = %d\n" , &zahl );
void main( ) {
}
scanf( " %d", &zahl );
!/* Eingabe */
int zahl = 0;
/* Ausgabe */ 345Falsch!
zahl = 345
Richtig!
scanf( "zahl = %d ", &zahl );
Leerzeichen
Die scanf-Funktion verlangt, dass der Text des Formatstrings genau
eingegeben wird.
Eingabe
Zahl = 0
Zahl = 345
scanf( "%d ", &zahl );
Leerzeichen Prof. Dr. Margarita Esponda
22
Programmbeispiel:
. . .
printf( "Bitte geben Sie ihr Geburtsdatum wie folgt ein: \n" );
printf( "dd.mm.jjjj: " );
rueckgabewert = scanf( "%d.%d.%d" , &tag, &monat, &jahr );
if ( rueckgabewert < 3 ) {
printf( "Falsches Format im Geburtagsdatum! \n" ); return 1;
}
if ( tag<0 || tag>31 || monat<0 || monat>12 || jahr<0 || jahr>2007 ) {
printf( "Unmoegliches Geburtsdatum! \n" );
return 1;
}
. . .
Die scanf-Funktion liefert als Rückgabewert die Anzahl der Daten, die erfolgreich gelesen, interpretiert und gespeichert wurden. Wenn der Rückgabewert gleich 0
ist, bedeutet es, dass keine der Eingabedaten richtig waren.
Prof. Dr. Margarita Esponda
23
Die Vergleichsoperatoren können nur mit den elementaren Datentypenvon C operieren und produzieren immer einen Wahrheitswert ( 0 oder 1).
falsch0Alles was ungleich null ist wahr
Wahrheitswerte in C
"<stdbool.h>"
1-bit type für die boolschen Werte false (0) und true (1)
C99 Standard
Prof. Dr. Margarita Esponda
24
Vergleichsoperatoren
#include <stdio.h>#include <stdbool.h>
int main() {
bool w1, w2, w3; int a = 3; int b = 3;
w1 = a<b; printf("w1 = %d \n", w1); w2 = true; printf("w2 = %d \n", w2); w3 = a!=b; printf("w3 = %d \n", w3);
system("PAUSE"); return 0;}
w1 = 0
w2 = 1
w3 = 0
Beispiel mit C99
Prof. Dr. Margarita Esponda
25
C99 Standard
"<stdint.h>"definiert die Datentypen
intn_t und uintn_t für n = 8, 16, 32, 64
intptr_t und uintptr_t sind Zeiger auf Integer in
Systemen mit genügend
großem Wertbereich für den
int-Datentyp.
intmax_t und uintmax_t, Datentyp mit maximalem
Grenzwert.
Prof. Dr. Margarita Esponda
26
C99 Standard
"<stdint.h>"
Hier sind auch entsprechende Makros definiert, um die
Grenzwerte der hier definierten Datentypen zu erfahren.Beispiel:
Datentyp Makros
int32_t INT32_MAX
INT32_MIN
uint64 INT64_MAX
Prof. Dr. Margarita Esponda
27
C99 Standard
"<inttypes.h>"
für jede intn_t und uintn_t in "<stdint.h>" definiert Makros
PRIdn, PRIin, PRIon, PRIun, PRIux, and PRIuX für die
Formatausgabe innerhalb der printf-Funktion.
Beispiel:
...
int32_t val = 20;
printf ("val = %08"PRId32"\n", val);
...
val = 00000020 Ausgabe:
Prof. Dr. Margarita Esponda
28
C99 Standard
Folgende unsichere Funktionen aus der <string.h> und
<stdio.h> Bibliothek sind in Pintos nicht dabei.
Unsichere Funktion Ersatzfunktion
strcpy strlcpy
strncpy strlcpy
strcat strlcat
strncat strlcat
strtok strtok_r
sprintf snprintf
vsprintf vsnprintf
Prof. Dr. Margarita Esponda
29
char *strcpy(char *dest, const char *src)
{
unsigned i;
for (i=0; src[i] != '\0'; ++i)
dest[i] = src[i];
dest[i] = '\0';
return dest;
} "Buffer overflows"
char *strncpy(char* dst, const char* src, size_t size);
Beispiel:
char *strncpy(char* dst, const char* src);
Verbesserte Funktion
30
Gültigkeitsbereich oder Sichtbarkeit von Variablen
#include <stdio.h>
int main(){ { int x = 7; x = x*x; { int y = 3; y = y*3 + x; } printf(" y=%d ", y); }
int z = 2; z = z*z*z;
printf("x=%d ", x ); printf("y=%d ", y ); printf("z=%d ", z ); return 0;}
Variablen von äußeren Blöcken sind in inneren Blöcken sichtbar.
Variablen von inneren Blöcken sind nach außen unsichtbar.
x ist hier sichtbar
Fehler! (y ist hier unsichtbar)
Fehler! (x ist hier unsichtbar)
Fehler! (y ist hier unsichtbar)
C99 Standard
Prof. Dr. Margarita Esponda
31
Arrays
#define MAX 9... int x[MAX]; x[4] =7;...
Mit einer define-Direktive können Konstanten definiert werden:
Jedes Vorkommen des Bezeichners MAX wird im Quellcode durch die Zahl 9 ersetzt.
0 1 2 3 4 5 6 7 8
x[0] x[1] x[2] x[3] x[4] x[5] x[6] x[7] x[8]
300 308 316 324 332 340 348 356 364
double x[9];
x
1.0 2.5 0.2 0.1 7.5 1.1 6.4 0.8 9.3
300
Speicheradresse
Wert
Index
Name
Die Variable X bekommt eine Adresse z.B. 300 als Wert
32
Arrays
In C-Programmen wird nicht immer geprüft, ob der Indexinnerhalb des erlaubten Index-Bereichs ist.!
Ein anderer typischer Fehler ist:
matrix [ i , j ] anstatt matrix [ i ] [ j ]
Das Ergebnis eines unerlaubten Zugriffs ist nicht definiert und verursacht unbemerkte Fehler.
Prof. Dr. Margarita Esponda
33
Datentyp struct
Verschiedene Deklarationsformen von Strukturen
Vereinbarung eines neuen Struktur-Datentyps, ohne Variablen zu deklarieren
struct Etikettname { komponente_1, . . . , komponente_n };
Vereinbarung von Variablen eines bereits deklarierten Struktur-Datentypnamens
struct Etikettname var_1, . . . , var_m ;
Prof. Dr. Margarita Esponda
34
Datentyp struct
struct Student {
int matrikelnummer;
int alter;
int semester;
};
Strukturdeklaration Das Student-Etikett kann später
zur Definition von Objekten mit
dieser Struktur verwendet werden.
struct Student s1, s2, s3;
Angesprochen werden die einzelnen Komponenten wie folgt:
strukturname.komponentname
Beispiel: s1.semester = 3;
Prof. Dr. Margarita Esponda
35
Datentyp struct
#include <stdio.h>
struct Punkt {
int x;
int y;
}
int main()
{
struct Punkt p1, p2, p3;
p1.x = 2;
p1.y = 3;
p2.x = p1.y;
p2.y = p1.x;
p3 = p2;
printf( " %d \n", p3.x );
}
Globale Datentypdefinition
Sichtbar innerhalb aller Funktionen
36
Strukturen kopieren
#include <stdio.h>
int main()
{
struct Punkt {
int x;
int y;
} p1;
p1.x = 2;
p1.y = 3;
struct Punkt p2 = p1;
}
Im Unterschied zu Arrays können Strukturen mit einer einfachen Zuweisung als Ganzes kopiert werden.
Analog dazu werden Strukturen an Funktionen als Ganzes übergeben oder zurückgegeben.
p1:
p2:
3
2
3
2
Prof. Dr. Margarita Esponda
37
typedef-Anweisung
Das typedef-Schlüsselwort erlaubt dem Programmierer,
einem Typ einen anderen Namen zu geben (Synonym)
Die typedef-Anweisung hat folgende Syntax:
typedef Datentypname Bezeichner;
Bekannter Datentypname
Beispiel:
typedef double Real;
Prof. Dr. Margarita Esponda
38
Schlüsselwort typedef
Beispiele: typedef char Zeichen
char und Zeichen sind Synonyme
ist äquivalent zu Zeichen a;char a;Die Definition
Zeichen ist ein Aliasname für char
Beispiel:typedef double Real;
Prof. Dr. Margarita Esponda
39
Schlüsselwort typedef
#include <stdio.h>
typedef double Gleitkommazahl;
int main() { Gleitkommazahl a, b; a = 50; b = 6.0; printf( "%f \n", a*b ); return 0;}
Beispiel:
Prof. Dr. Margarita Esponda
40
Zeiger (Pointer) in C
111010101
011011101
111110101
111010101
011010101
011011101
011010111
011111101
111010101
011010101
011010101
Prof. Dr. Margarita Esponda
41
Zeiger ( Pointer )
Zeiger sind Verweise auf Daten.
Zeiger werden deklariert, indem ihrem Namen jeweils ein Stern vorangestellt wird.
Beispiel:
int* zeigtAufInt; // Zeiger auf int
int *zeigtAufInt; // Zeiger auf int
int*zeigtAufInt; // Zeiger auf int
int * zeigtAufInt; // Zeiger auf int
C ist eineformatfreieSprache!
Zeigervariablen sind Variablen, deren Wert eine Speicheradresse ist.
1008096p 3x: 1008096
Prof. Dr. Margarita Esponda
42
Zeiger ( Pointer )
Zur Manipulation von Zeigern werden die zwei einstelligen Operatoren & und * verwendet.
Der einstellige Operator & liefert die Adresse von einem Datum bzw. einem Zeiger auf ein Datum.
Beispiel:
x_adresse = &x; /* x_adresse zeigt auf x */
int *x_adresse; /* x_adresse ist ein Zeiger */
/* auf eine Variable vom Typ int */
int x = 3; /* x ist eine int-Variable */
y = *x_adresse; /* y bekommt die Zahl 3 */
int y = 5; /* y ist eine int-Variable */
43
Zeiger ( Pointer ) in C
.
.
.
.
int y;
int* p;
y = 10;
p = NULL;
p = &y;
y
p
10
100016
100016
100020
100024
100028
100032
Prof. Dr. Margarita Esponda
44
p2 : 100016
d : 100036
Zeigern in C
p1 : 100000
5,8
.
.
.
.
100036
Die Variable d wurde
indirekt verändert.
double* p1= NULL;
double* p2 = NULL;
double d = 3.8;
p1 = &d ;
p2 = p1 ;
*p1 = *p1 + 1;
*p2 = *p2 + 1;
100036
Prof. Dr. Margarita Esponda
45
Strukturen
#include <stdio.h>
typedef struct {
char *name;
int alter;
} Kunde;
int main( void ) {
Kunde k1;
k1.name = "Peter Menzel";
k1.alter = 32;
printf("%s \n", k1.name);
printf("%d \n", k1.alter);
return 0;
}
Datentyp.
.
.
.
k1: 1000
K1.name: 1024 P e t e
r M e
n z e l
\0
32
1024
Prof. Dr. Margarita Esponda
46
Punkt p1;
Punkt p2;
p2.x = 0;
p2.y = 0;
p1.x = 10;
p1.y = 25;
Punkt * p_p1 = &p1;
p_p1 = p_p2;
p_p2 -> x = 7;
p_p1 -> y = 39;
Punkt * p_p2 = &p2;
Arithmetik mit Zeigern
p_p2
typedef struct {
int x;
int y;
} Punkt;.
.
.
.
p1: 1000
p2: 1024
p_p1
.
.
.
.
1024
10 25
7 39
1024
Prof. Dr. Margarita Esponda
47
Arithmetik mit ZeigernDie Sprache C erlaubt die Addition oder Subtraktion von Zeigern und ganzen Zahlen.
Beispiel
#include <stdio.h>
main() {
int zahl1 = 3, zahl2 = 7;
int* zeiger_auf_zahl;
zeiger_auf_zahl = &zahl1;
zeiger_auf_zahl = zeiger_auf_zahl - 1;
printf( " %d \n", *zeiger_auf_zahl );
zeiger_auf_zahl = zeiger_auf_zahl + 1;
printf( " %d \n", *zeiger_auf_zahl );
}
.
.
.
.
Die Variable zeiger_auf_zahl zeigt nach der Zuweisung auf das nächste Objekt vom Typ int, d.h. der Wert von zeiger_auf_zahl verändert sich um 4 Bytes.
Zufällig befindet sich an dieser Stelle eine Variable vom Typ int. Aber normalerweise ist solche Zeiger-Arithmetik fehlerhaft.
zahl2 : 100
zahl1 : 104 3
7int zahl1 = 3, zahl2 = 7;
zeiger_auf_zahl = &zahl1;
104zeiger_auf_zahl : 96int* zeiger_auf_zahl;
100
zeiger_auf_zahl = zeiger_auf_zahl - 1;
7printf( " %d \n", *zeiger_auf_zahl );
3printf( " %d \n", *zeiger_auf_zahl );
104
zeiger_auf_zahl = zeiger_auf_zahl + 1;
Prof. Dr. Margarita Esponda
48
Beispiel
#include <stdio.h>
int main() { double d; double* p_d; d = 1.0; p_d = &d; printf ( " %u \n", &p_d ); printf ( " %u \n", &d ); printf ( " %u \n", p_d ); printf ( " %p \n", &d ); printf ( " %lf \n", d ); printf ( " %lf \n", *p_d); *p_d = *p_d + 3.0; printf ( " %lf \n", d ); printf ( " %u \n", &d ); p_d = p_d - 1; /* Fehlerhaft */ printf ( " %p \n", p_d ); *p_d = 9.0; d = 11.0; printf ( " %lf \n", *p_d );}
.
.
.
.
p_d : 1024
d : 1032
Kleinere
Speicheradressen
/* Programmabsturz! */
4 Bytes
10241032
1032
408 Hex
1.0
1.0
1032
1.04.0
4.0
1032
1024
1024
9.0
11.0
Prof. Dr. Margarita Esponda
49
...
int a = 10;
int* p_a = &a;
printf(" p_a = %p \n", p_a);
int* pp_a = p_a;
*p_a++;
printf(" a = %d \n", a);
printf(" p_a = %p \n", p_a);
a = a++;
printf(" a = %d \n", a);
(*pp_a)++;
++*pp_a;
(*pp_a)++;
++(*pp_a);
printf(" *pp_a = %d \n", *pp_a);
...
Beispiel:
Ausgabe:?
50
Vorsicht mit Zeiger als Rückgabewert in C
int *wrongReturn() { int a = 50; return &a;}
void foo() { int x = 100;}
int main () { int *xp; xp = wrongReturn(); printf ( "*xp == %d\n", *xp ); foo(); printf ( "*ip == %d\n", *xp );}
*xp == 50
*xp == 100
Ausgabe?
Prof. Dr. Margarita Esponda
51
Strings
Am Ende eines Strings ist immer das Zeichen ´\0´.
( dahinter steckt der Wert 0 )
char* s = "text";
char* s2 = "TEXT";
s = s2;
Speicher
s = s2 + 1;
Adresse1
't' 'e' 'x' 't' '\0's
Adresse2 'T' 'E' 'X' 'T' '\0's2
't' 'e' 'x' 't' '\0'
'T' 'E' 'X' 'T' '\0'
Adresse2
Adresse2
s
s2
'T' 'E' 'X' 'T' '\0'
Adresse2+1
Adresse2
s
s2
Müll
Prof. Dr. Margarita Esponda
52
C99 Standard
"<stddef.h>"
"z" Für size_t (e.g. "%zu").
"t" Für ptrdiff_t (e.g. "%td").
#include <stdlib.h>
int main()
{
int buffer[4];
ptrdiff_t dif = (&buffer[3]) - buffer;
dif = buffer -(&buffer[3]);
}
Beispiel:
53
Speicher-Operatoren
Eckige Klammern [ ] y[3] Gibt den Wert des Elements 3
des y-Arrays an.
Operator Zeichen Beispiel Bedeutung
Punktoperator . z.a Gibt den Wert der Komponente a der Struktur z an.
Zeigeroperatoren &
*
&x
*a
Gibt die Adresse der Variablen x an.
Gibt den Wert des in Adresse a gespeicherten Objektes an.
Pfeiloperator -> z->a Gibt den Wert der Komponente a der Struktur, dessen Adresse in z gespeichert ist, an.
Prof. Dr. Margarita Esponda
54
Ein typisches C-Programm besteht aus mehreren C-Quelldateien und
mehreren Header-Dateien.
Größere Programme in C
C-Quelldateien beinhalten die Funktionsdefinitionen sowie die globalen
Deklarationen innerhalb einer C-Quelldatei.
Wenn die Funktionen einer C-Quelldatei in anderen C-Quelldateien
benutzt werden sollten, muss ihre entsprechende header-Datei definiert
werden.
In der header-Datei werden dann die entsprechenden
Funktionsprototypen deklariert, sowie Datentypen und globale
Definitionen, die in anderen C-Quelldateien benutzt werden dürfen.
Prof. Dr. Margarita Esponda
55
C-Programme
QuellcodeDatei
HeaderQuellcode
QuellcodeDatei
HeaderQuellcode
QuellcodeDatei
ObjektcodeDatei
QuellcodeDatei
ObjektcodeDatei
Compiler Compiler
LinkerLaufzeit-Bibliothek
AusführbaresProgramm
QuellcodeDatei
CQuellcode
QuellcodeDatei
CQuellcode
QuellcodeDatei
CQuellcode
QuellcodeDatei
ObjektcodeDatei
Compiler
Header-Dateien müssen nicht kompiliert werden.
Prof. Dr. Margarita Esponda
56
Die #include-Direktive
Die #include-Direktive hat zwei Formen.
#include <Dateiname>
#include "Dateiname"
Wenn die header-Datei Teil der C-Bibliothek ist und sich dann entsprechend in dem dafür definierten
include-Verzeichnis befindet
Für alle anderen header-Dateien inklusive selbstgeschriebener Dateien.
Der Compiler sucht dann zuerst in dem
angegebenen Verzeichnis oder in dem aktuellen Verzeichnis, wenn nur ein Dateiname angegeben
worden ist und dann in dem Verzeichnis, wo
normalerweise die include-Dateien sind.
Prof. Dr. Margarita Esponda
57
Die #include-DirektiveBeispiele:
#include "c:\c_programs\utilities.h" /* Windows Verzeichnis */
#include "/c_programs/utilities.h" /* UNIX oder LINUX */
Keine Geräte- oder absolute Verzeichnisinformation in dem include zu benutzen, weil die Übersetzung des Programms in einen anderen Rechner dadurch erschwert wird.
Beispiele: #include "d:utils.h"
#include "\c_progs\utils.h"
Ein Verzeichnispfad relativ zu dem aktuellen Verzeichnis ist immer besser.
Beispiele: #include "utils.h"
#include ". .\include\utils.h"
besser
schlecht
Prof. Dr. Margarita Esponda
58
Gemeinsame Verwendung von Funktionen
#include <stdio.h>
/** Prototypen ***************/
int klammer ( int zeichen );int leerzeichen( int zeichen );
int buchstabe( int zeichen );
int ziffer( int zeichen );
int operator( int zeichen );
int sonstige_zeichen( int zeichen );
/** Rahmenprogramm **********/
int main() {
int zeichen;
int total = 0, summe = 0;
int buchstabenzaehler = 0; int leerzeichenzaehler = 0;
int operatorenzaehler = 0;
int ziffernzaehler = 0;
int klammernzaehler = 0;
int sonstige_zeichenzaehler = 0; . . . .
/** Funktionen *****************/
. . . .
/** Prototypen ***************/
int klammer (int zeichen);
int leerzeichen( int zeichen );
int buchstabe( int zeichen );int ziffer( int zeichen );
int operator( int zeichen );
int sonstige_zeichen( int zeichen );
#include "zeichen.h"/** Funktionen *****************/
int klammer( int zeichen ) {
return zeichen=='{' || zeichen=='}' || zeichen=='[' || zeichen== ']' ||
zeichen==‚)' || zeichen=='(' ;
}
int leerzeichen( int zeichen ) {
return zeichen==' ' || zeichen=='\t' || zeichen=='\n';
}
. . .
zeichen.h
zeichen.c
Beispiel:
59
Gemeinsame Verwendung von Funktionen
/** Prototypen ***************/
int klammer (int zeichen);
int leerzeichen( int zeichen );
int buchstabe( int zeichen );int ziffer( int zeichen );
int operator( int zeichen );
int sonstige_zeichen( int zeichen );
/** Funktionen *****************/
int klammer( int zeichen ) {
return zeichen=='{' || zeichen=='}' ||
zeichen=='[' || zeichen== ']' || zeichen==‚)' || zeichen=='(' ;
}
int leerzeichen( int zeichen ) {
return zeichen==' ' || zeichen=='\t' || zeichen=='\n';
}
. . .
zeichen.h
zeichen.c
#include <stdio.h>
#include "zeichen.h"
/** Rahmenprogramm **********/
int main() {
int zeichen; int total = 0, summe = 0;
int buchstabenzaehler = 0;
int leerzeichenzaehler = 0;
int operatorenzaehler = 0;
int ziffernzaehler = 0; int klammernzaehler = 0;
int sonstige_zeichenzaehler = 0;
. . . .
zeichen_zaehler.c
Prof. Dr. Margarita Esponda
60
Gemeinsame "define"-Direktiven oder "typdef"-Definitionen werden auch oft verwendet.
#define TRUE 1#define FALSE 0
typedef int Bool;
#include "zeichen.h"
Bool klammer( char zeichen ) { return zeichen=='{' || zeichen=='}' || zeichen=='[' || zeichen== ']' || zeichen==‚)' || zeichen=='(' ;}. . .
boolean.h
zeichen.c#include "boolean.h"
Bool klammer ( char zeichen );Bool leerzeiche ( char zeichen );Bool buchstabe ( char zeichen );. . .
zeichen.h
Prof. Dr. Margarita Esponda
61
Probleme bei eingebetteten
include-Dateien #define TRUE 1#define FALSE 0
typedef int Bool;
boolean.h
#include <stdio.h>#include "zeichen.h"#include "boolean.h"
int main() { . . . .
#include "boolean.h"
Bool klammer( int zeichen ) { return zeichen=='{' || zeichen=='}' || zeichen=='[' || zeichen== ']' || zeichen==‚)' || zeichen=='(' ;}. . .
zeichen.c
boolean.h wird einmal explizit mit hineingenommen und einmal indirekt durch zeichen.h
Die doppelte typdef kann Fehler beim Übersetzen verursachen.
zeichen_zaehler.c
!
#include "boolean.h"
Bool klammer (char zeichen);
Bool leerzeiche (char zeichen);
Bool buchstabe (char zeichen);. . .
zeichen.h
Prof. Dr. Margarita Esponda
62
Lösung:
Geschützte header-Dateien
#ifndef BOOLEAN_H#define BOOLEAN_H
#define TRUE 1#define FALSE 0
typedef int Bool;
#endif
boolean.hDieser Name ist freigewählt
Prof. Dr. Margarita Esponda
63
C-Programme konstruieren, die aus mehreren Dateien bestehen
Übersetzen (Compiler)
Header-Dateien müssen nicht übersetzt werden.
Binden (Linker)
cc –o zz zeichen.c zeichen_zaehler.c
Zuerst werden die C-Dateien übersetzt, dann werden die
übersetzten o-Dateien dem Linker weitergegeben.
Der Linker kombiniert alle o-Dateien in eine einzige ausführbare
Datei.
Prof. Dr. Margarita Esponda
64
C-Programme konstruieren, die aus mehreren Dateien
bestehen
Makefiles
Die Makefiles sind erfunden worden, um dem Programmierer
das Übersetzen von großen C-Programmen, die aus vielen
Dateien bestehen, zu erleichtern.
Mit Hilfe von Makefiles werden außerdem nur die C-Quelldateien
neu übersetzt, die verändert worden sind.
In diesen Makefiles werden die Dateiabhängigkeiten mit
einer sehr einfachen Syntax deklariert.
Prof. Dr. Margarita Esponda
65
C-Programme aus mehreren Dateien konstruieren
Beispiel eines Makefiles:
LINUX
zzz: zeichen.o zeichen_zaehler.o cc –o zzz zeichen.o zeichen_zaehler.o
zeichen.o: zeichen.c zeichen.h boolean.h cc -c zeichen.c
zeichen_zaehler.o: zeichen_zaehler.c boolean.h cc –c zeichen_zaehler.c
Makefiles werden heutzutage immer von der Programmierumgebung automatisch generiert.
Unser Ziel ist es, die Datei zzz zu erzeugen.
Diese ist abhängig von zeichen.o und zeichenzaehler.o.
Prof. Dr. Margarita Esponda
66