• Keine Ergebnisse gefunden

¨Ubungspaket 17 Der gcc Compiler

N/A
N/A
Protected

Academic year: 2021

Aktie "¨Ubungspaket 17 Der gcc Compiler"

Copied!
10
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Ubungspaket 17 ¨ Der gcc Compiler

Ubungsziele: ¨

1. Sicherer Umgang mit gemischten Ausdr¨ ucken

2. Herleiten der unterschiedlichen Datentypen in gemischten Aus- dr¨ ucken

3. Kenntnis ¨ uber die implizite Durchf¨ uhrung von Typanpassungen Skript:

Kapitel: 38, 39 und 40 Semester:

Wintersemester 2021/22 Betreuer:

Thomas, Tim und Ralf Synopsis:

Eines haben wir alle bis jetzt sicherlich gelernt, durch Eintippen von

gcc quelltext.c wird ein C-Programm in Maschinencode ¨ ubersetzt

und damit lauff¨ ahig gemacht. Doch wenn etwas schiefgeht, fangen die

Probleme an. Der Compiler selbst ist wie die meisten Programmier-

werkzeuge ein recht komplexes Programm. Daher f¨ allt es vielen Pro-

grammieranf¨ angern sehr schwer, den ¨ Uberblick ¨ uber die einzelnen Tei-

le des Compilers zu behalten und die aufgetretenen Fehlerursachen zu

lokalisieren. Um hier Abhilfe zu schaffen, schauen wir uns in diesem

Ubungspaket den Compiler ¨ gcc und seine Komponenten ein wenig ge-

nauer an.

(2)

Teil I: Stoffwiederholung

Aufgabe 1: Grobaufbau des gcc Compilers

Durch den Aufruf gcc datei.c werden eigentlich vier gr¨oßere Programme nacheinander aufgerufen. Benenne diese vier Programme und erl¨autere kurz, was ihre Aufgaben sind.

1. Programm: Pr¨aprozessor

Funktion: Der Pr¨aprozessor erf¨ullt im Wesentlichen drei Funktionen:

1. Der Pr¨aprozessor ersetzt all#include-Direktiven durch die an- gegebenen Dateien (diese Dateien werden vollst¨andig in den Quelltext eingef¨ugt).

2. Ersetzen aller #define-Makros durch ihre entsprechenden De- finitionen.

3. ¨Ubersetzen bzw. entfernen aller Anweisungen zwischen den

#ifdef, #ifndef,#else und #endif Direktiven.

2. Programm: Eigentlicher Compiler (der C- ¨Ubersetzer)

Funktion: In dieser Phase wird das C-Programm in denjenigen Assembler-Code ubersetzt, der zum gew¨¨ ahlten Prozessor geh¨ort. Das Ergebnis ist also eine Datei, die prozessorspezifisch ist.

Ergebnis: eine Datei mit der Endung.s 3. Programm: Assembler

Funktion: Der Assembler wandelt den (prozessorspezifischen) Assembler-Code, der noch

”lesbare“ Anweisungen enth¨alt in Maschinencode um. Das Ergebnis ist eine Datei, die nur noch aus unverst¨andlichen Nullen und Einsen besteht, die ¨ublicherweise zu hexadezimalen Zahlen zu- sammengefasst werden.

Ergebnis: eine Datei mit der Endung.o 4. Programm: Linker

Funktion: Der Linker f¨ugt den Maschinencode und alle verwendeten Bibliothe- ken zu einem einzigen lauff¨ahigen Programm zusammen. Erst dieses Programm kann vom Prozessor (in Zusammenarbeit mit dem Be- triebssystem) auch wirklich ausgef¨uhrt werden.

Ergebnis: eine ausf¨uhrbare Datei mit dem angegeben Namen oder a.out

(3)

Aufgabe 2: Die Syntax der Pr¨ aprozessor-Direktiven

Erkl¨are kurz in eigenen Worten die Syntax der C-Pr¨aprozessor-Direktiven:

Alle C-Pr¨aprozessor-Direktiven fangen mit einem Doppelkreuz # an, werden von einem der Schl¨usselw¨orter include,define,ifdef,ifndef,elseoderendifgefolgt und meis- tens mit einem Argument (Parameter) abgeschlossen. Zwischen dem Zeilenanfang, dem Doppelkreuz #, dem Schl¨usselwort und den Argumenten d¨urfen beliebig viele Leerzei- chen, Tabulatoren und Kommentare eingef¨ugt werden. Ferner ist zu beachten, dass eine C-Pr¨aprozessor-Direktive in einer Zeile abgeschlossen wird, es sei denn, die Zeile wird mit einem Backslash ‘\’abgeschlossen.

Aufgabe 3: Die Pr¨ aprozessor-Direktiven

Erkl¨are jede Pr¨aprozessor-Direktive anhand je eines oder zweier Beispiele:

1. #include

Mittels der Direktive#includekann man andere Dateien einbinden. Die #include wird dadurch vollst¨andig durch den Inhalt der angegebenen Datei ersetzt.

#include <stdio.h> // Einbinden der Standard Ein-/Ausgabe

#include "my.h" // Einbinden der eigenen Datei my.h 2. #define

Mittels #define kann man einfache Labels und ganze Makros definieren:

#define NAME Cool // mein Name

#define HiThere( x ) Hi Daddy x // meine Anrede HiThere( NAME )

Ergibt: Hi Daddy Cool

3. #ifdef ... #else ...#endif

Die #ifdef-Direktive erlaubt es, Dinge in Abh¨angigkeit anderer zu ¨ubersetzen.

#ifdef my header

#else

#include "my-header.h"

#endif

In diesem Falle wird die Dateimy header.hnur dann eingebunden, sofern das Label my header noch nicht definiert wurde. Zwischen #if, #else und #endif k¨onnen beliebige Anweisungen stehen. In den.h-Dateien werden oftmals Labels der obigen Form definiert, um das mehrmalige Ausf¨uhren eines#include zu vermeiden.

(4)

Teil II: Quiz

Aufgabe 1: Die Definition von

” Namen“

Gegeben sei folgendes Programmst¨uck:

1 # d e f i n e Tag M o n t a g /* m e i n A r b e i t s t a g */

2 # d e f i n e W O C H E 34 /* m e i n e U r l a u b s w o c h e */

3

4 # d e f i n e T e l e f o n 0 3 8 1 498 72 51

5 # d e f i n e FAX /* 0 0 4 9 */ 0 3 8 1 498 118 72 51 6

7 # d e f i n e F e r i e n 8

9 # d e f i n e MSG " h e u t e g e h t es mir s e h r gut "

10

11 # d e f i n e N A M E 1 ein n a m e 12 # d e f i n e N A M E 2 N A M E 1 13

14 # d e f i n e Z W E I _ Z E I L E N ZEILE -1 /* das ist die e r s t e Z e i l e 15 # d e f i n e Z W E I T E _ Z E I L E h i e r ist Z e i l e 2 */

16

17 # d e f i n e E N D E " j e t z t ist s c h l u s s "

Finde heraus, welche Namen in obigem Programmst¨uck definiert werden und welche Werte diese haben. Trage die definierten Namen nebst ihrer Werte in folgende Tabelle ein.

Zeile Name Wert

1 Tag Montag

2 WOCHE 34

4 Telefon 0381 498 72 51 5 FAX 0381 498 118 72 51 7 Ferien

9 MSG "heute geht es mir sehr gut"

11 NAME1 ein name 12 NAME2 ein name 14 ZWEI ZEILEN ZEILE-1

15 --- Hier wird kein Name definiert, denn diese Zeile ist auskommentiert 17 ENDE "jetzt ist schluss"

(5)

Aufgabe 2: Definition von Makros

Gegeben sei folgendes Programmst¨uck:

1 // m a c r o i m p l e m e n t i o n of the f o r m u l a : 2* x + 3* y 2

3 # d e f i n e F o r m u l a _ 1 ( x , y ) 2 * x + 3 * y 4 # d e f i n e F o r m u l a _ 2 ( x , y ) 2 * ( x ) + 3 * ( y ) 5

6 i = F o r m u l a _ 1 ( 1 , 2 ) ; j = F o r m u l a _ 2 ( 1 , 2 ) ; 7 i = F o r m u l a _ 1 ( 4 , 2 ) ; j = F o r m u l a _ 2 ( 4 , 2 ) ; 8 i = F o r m u l a _ 1 ( 1+2 , 2+3 ) ; j = F o r m u l a _ 2 ( 1+2 , 2+3 ) ; 9 i = F o r m u l a _ 1 ( 2+1 , 3+2 ) ; j = F o r m u l a _ 2 ( 2+1 , 3+2 ) ; Notiert in der folgenden Tabelle die Werte der Parameterx und y, das erwartete Ergebnis res = 2 * x + 3 * y, sowie die Resultate, die in den Variableni undjabgelegt werden:

Zeile x y res i j

6 1 2 8 8 8

7 4 2 14 14 14

Zeile x y res i j

8 3 5 21 13 21

9 3 5 21 16 21

Wie werden die Makros vom Pr¨aprozessor expandiert (ersetzt)?

6 i = 2 * 1 + 3 * 2; j = 2 * (1) + 3 * (2) ;

7 i = 2 * 4 + 3 * 2; j = 2 * (4) + 3 * (2) ;

8 i = 2 * 1+2 + 3 * 2 + 3 ; j = 2 * ( 1 + 2 ) + 3 * ( 2 + 3 ) ; 9 i = 2 * 2+1 + 3 * 3 + 2 ; j = 2 * ( 2 + 1 ) + 3 * ( 3 + 2 ) ;

Uberpr¨¨ uft eure Annahme durch Aufruf des Pr¨aprozessors. Nehmen wir an, ihr habt die paar Zeilen abgetippt und in der Datei quiz.c gespeichert. Dann einfach cpp quiz.c oder alternativ gcc -E quiz.c aufrufen; die Ergebnisse erscheinen dann direkt auf dem Bildschirm. ¨Uberpr¨uft nochmals eure Ergebnisse, wie ihr sie in obiger Tabelle eingetra- gen habt. Erkl¨art, sofern sich Unterschiede auftun, was hierf¨ur die Ursachen sind. Welche Schlussfolgerungen zieht ihr daraus f¨ur die Definition von Makros?

Ursachen: In der Makro-Definition Formula 1 werden die beiden Parameter x und y nicht geklammert. Dadurch kann es sein, dass bei ¨Ubergabe von einfachen arithmetischen Ausdr¨ucken die einzelnen Bestandteile aufgrund der Pr¨azedenzregeln nicht so ausgewertet werden, wie man es erwartet.

Problembehebung: Bei der Definition (der Implementierung) von Makros sollten die Parameter stets geklammert werden. So werden die Parameterimmer zuerst ausgewertet und dann mit anderen Parametern verkn¨upft.

(6)

Aufgabe 3: Einbinden von (Header-) Dateien

Gegeben sei folgendes Programmst¨uck:

1 # i n c l u d e < s t d i o . h >

2 # i n c l u d e < m a t h . h >

3

4 # d e f i n e C _ T Y P E S < c t y p e . h >

5 # i n c l u d e C _ T Y P E S 6

7 //# i n c l u d e < s i g n a l . h >

Welche Dateien werden dadurch vom Pr¨aprozessor eingebunden?

Folgende Systemdateien (aus /usr/include) werden eingebunden:

stdio.h, math.h und ctype.h

Die Datei signal.hwird nicht eingebunden, da diese Zeile auskommentiert ist.

Aufgabe 4: Bedingtes ¨ Ubersetzen

Gegeben sei folgendes Programmst¨uck:

1 # d e f i n e A 4 7 1 1 2

3 # i f d e f A

4 # d e f i n e N1 17

5 # i f d e f B

6 # d e f i n e N2 2

7 # e l s e

8 # d e f i n e N2 4

9 # e n d i f 10 # e l s e

11 # d e f i n e N1 12

12 # d e f i n e N2 -3

13 # e n d i f 14

15 int i = N1 * N2 ;

Welche Labels werden mit welchen Werten definiert, welchen Wert erh¨alt die Variable i?

Zeile Label Wert

1 A 4711

4 N1 17

Zeile Label Wert

8 N2 4

15 i 68

(7)

Teil III: Fehlersuche

Aufgabe 1: Praktische Fehlersuche

Das folgende Programm soll den Text Fehler beheben drei Mal ausgeben, jeweils eine Ausgabe pro Zeile. Diesmal war unser Starprogrammierer Dr. Bit-Byte zugange. Aber auch ihm sind einige Fehler unterlaufen. Finde und korrigiere diese. Zeige anschließend mittels einer Handsimulation, dass dein Programm korrekt arbeitet.

1 # d e f i n e A N Z A H L 3;

2 # d e f i n e L O O P _ C O N D I T I O N ( f e h l e r >= 0) 3 # d e f i n e MSG " F e h l e r " e h e b e n "

4

5 int m a i n ( int argc , c h a r ** a r g v ) ;

6 {

7 int f e h l e r = A N Z A H L ;

8 do

9 p r i n t f ( MSG ) ; f e h l e r = - 1;

10 w h i l e ( L O O P _ C O N D I T I O N ) ;

11 r e t u r n 0;

12 }

Zeile Fehler Erl¨auterung Korrektur

0 #include fehlt

Die Datei stdio.h wird nicht eingebunden. Wir brau- chen sie aber, da wir etwas ausgeben wollen.

#include

<stdio.h>

. . . .

1 Das Semikolon scheint zu viel zu sein. Aber in der Ini- tialisierung in Zeile 7 ist es dennoch ok (aber unsch¨on).

3

. . . .

2 >= statt

>

Im Verbindung mit der do-while-Schleife in den Zeilen 8 bis10 ergeben sich vier statt drei Schleifendurchl¨aufe.

(fehler>0)

. . . .

3 " statt b Hier scheint ein einfacher Tippfehler vorzuliegen. beheben

. . . .

5 ; zu viel Bei der Funktionsdefinition darf am Ende kein ; ste- hen. Dieses ist nur bei Funktionsdeklarationen erlaubt, die dem Compiler den Namen und die Signatur der Funk- tion bekannt machen.

argv )

. . . .

8/10 {} fehlen Da die do-while-Schleife mehr als eine Anweisung aus- f¨uhren soll, m¨ussen sie durch{} geklammert werden.

{... }

. . . .

9 fehler fehlt

Da der Wert der Variablenfehlerum eins reduziert wer- den soll, muss sie auch auf der rechten Seite auftauchen.

fehler = fehler - 1

(8)

Programm mit Korrekturen:

1 # i n c l u d e < s t d i o . h >

2

3 # d e f i n e A N Z A H L 3

4 # d e f i n e L O O P _ C O N D I T I O N ( f e h l e r > 0)

5 # d e f i n e MSG " F e h l e r b e h e b e n \ n "

6

7 int m a i n ( int argc , c h a r ** a r g v )

8 {

9 int f e h l e r = A N Z A H L ;

10 do {

11 p r i n t f ( MSG ) ;

12 f e h l e r = f e h l e r - 1;

13 } w h i l e ( L O O P _ C O N D I T I O N ) ;

14 r e t u r n 0;

15 }

Vor der Handsimulation ist es eine gute ¨Ubung, sich das Programm zu ¨uberlegen, das der Pr¨aprozessor aus der.c-Datei generiert:

Ergebnis nach Abarbeitung durch den Pr¨aprozessor:

7 int m a i n ( int argc , c h a r ** a r g v )

8 {

9 int f e h l e r = 3;

10 do {

11 p r i n t f ( " F e h l e r b e h e b e n \ n " ) ;

12 f e h l e r = f e h l e r - 1;

13 } w h i l e ( ( f e h l e r > 0) ) ;

14 r e t u r n 0;

15 }

Handsimulation:

Zeile 9 11 12 13 11 12 13 11 12 13 14

fehler 3 2 2 1 1 0 0

printf() Fehler beheben

Fehler beheben

Fehler beheben

(9)

Teil IV: Anwendungen

Aufgabe 1: Fehler Finden und Eliminieren

Es ist ganz normal, dass man beim Entwickeln und Eintippen eines Programms viele Tipp- fehler macht. Da der Compiler ein recht pingeliger Zeitgenosse ist, findet er viele dieser Tippfehler und gibt entsprechende Fehlermeldungen und Hinweise aus. Die Kunst besteht nun darin, dieser Ausgaben richtig zu deutenund die wirklichen Fehlerursachen zu finden.

Um dies ein wenig zu ¨uben, greifen wir nochmals das fehlerhafte Programm aus dem vor- herigen Abschnitt (vorherige Seite) auf, wobei wir davon ausgehen, dass ihr sowieso alle Fehler gefunden habt. Arbeite nun wie folgt:

Arbeitsanleitung:

1. Tippe das fehlerhafte Programm ab und speichere es in einer Datei deiner Wahl.

2. ¨Ubersetze das fehlerhafte Programm mittels gcc.

3. Fahre mit Arbeitsschritt6fort, falls der Compiler keine Fehlermeldung oder Warnung ausgegeben hat.

4. Lese die erste(n) Fehlermeldung(en) aufmerksam durch und korrigiere einen Fehler.

5. Gehe zur¨uck zu Arbeitsschritt 2.

6. Starte das Programm und ¨uberpr¨ufe, ob es korrekt arbeitet.

7. Sollte das Programm korrekt arbeiten gehe zu Schritt 10 8. Korrigiere einen inhaltlichen Fehler (Semantikfehler).

9. Gehe zur¨uck zu Arbeitsschritt 2.

10. Fertig!

Hinweis: Diese Herangehensweise empfiehlt sich auch in Zukunft :-) !

(10)

Aufgabe 2: Eigene Makro-Definitionen

Definiere je ein Makro f¨ur die folgenden drei Formeln (L¨osungen siehe unten):

f(x) = 3x2+x/2−1 g(x, y) =x2−3xy+y2 h(x, y, z) = 2x3−3y2+ 2z

Zu welchen Ergebnissen f¨uhren die folgenden drei Aufrufe (Einsetzen)?

Aufruf Resultat Mathematik Resultat C-Programm

f(1 +z) 3(1 +z)2+ (1 +z)/2−1 3*(1 + z)*(1 + z) + (1 + z)/2 - 1 g(x, A+ 1) x2−3x(A+ 1) + (A+ 1)2 x*x - 3*x*(A + 1) + (A + 1)*(A + 1) h(a, b, a+b) 2a3−3b2+ 2(a+b) 2*a*a*a - 3*b*b + 2*(a + b)

Uberpr¨¨ uft eure Makro-Definitionen durch Eintippen eines kleinen Programmst¨ucks und Aufruf des Pr¨aprozessors (entweder mittels cpp <datei>.c oder gcc -E <datei>.c).

Programmst¨uckchen:

1 # d e f i n e f ( x ) 3*( x ) *( x ) + ( x ) /2 - 1

2 # d e f i n e g ( x , y ) ( x ) *( x ) - 3*( x ) *( y ) + ( y ) *( y ) 3 # d e f i n e h ( x , y , z ) 2*( x ) *( x ) *( x ) - 3*( y ) *( y ) + 2*( z ) 4

5 f (1 + z ) 6 g ( x , A + 1) 7 h ( a , b , a + b )

Ausgabe des Pr¨aprozessors:

1 3 * ( 1 + z ) *(1 + z ) + (1 + z ) /2 - 1

2 ( x ) *( x ) - 3*( x ) *( A + 1) + ( A + 1) *( A + 1) 3 2*( a ) *( a ) *( a ) - 3*( b ) *( b ) + 2*( a + b )

Referenzen

ÄHNLICHE DOKUMENTE

Schl¨ usselw¨ orter Java Views Java Browsing Project Builder

Der Beweiser offenbart dem Verifizierer sein Paßwort w, dieser berechnet f (w) und ¨uberpr ¨uft, ob der Wert in der Benutzer-Datei f ¨ur den Beweiser

Ziel: A und B wollen (mithilfe von ¨offentlichen Schl ¨usseln, Master Keys) einen gemeinsamen, geheimen Schl ¨ussel (Session Key) bestimmen.. Dieser kann dann beispielsweise f

CLINK automatically searches the library after all other CRL files given on the command line have been searched once; thus, any functions you explicitly define

[r]

[r]

This is necessary in the SMALL model for the dynamic memory allocation routines, which are used by the standard I/O library to function correctly.. The size of

→ Neben Compiler werden f¨ ur das ¨ Ubersetzen des Quelltextes der Pr¨ aprozessor und der Linker ben¨ otigt.. Pr¨ aprozessor: einfacher Text¨ ubersetzer der Makroanweisungen