Etapy kompilacji Przetwarzanie wstępne — preprocessing

Transkrypt

Etapy kompilacji Przetwarzanie wstępne — preprocessing
Etapy kompilacji
#define ILE 100
...
for (i=0; i<ILE; i++)
...
ude
przetwa- #incl
rzanie #defin
e
wstępne
#if
❄
...
for (i=0; i<100; i++)
...
Przetwarzanie wstępne
— preprocessing
......................kompilacja
..............................
..............
właściwa .................
....
...
...
...❄
✶ binaria
dane
q
wyniki
Wykład 7. Przetwarzanie wstępne, str. 2
Przetwarzanie wstępne — preprocessing
• Przed właściwą kompilacją tekst programu jest przekształcany zgodnie
z dyrektywami programisty.
• To przekształcenie jest czysto tekstowe — preprocesor nie zajmuje się
logiką programu. Nieostrożnie napisane dyrektywy mogą spowodować,
że program nie da się skompilować, albo będzie działał błędnie.
• Podstawowe dyrektywy:
– #include — włączyć plik
– #define — zastąpić w tekście programu
– #if — kompilować warunkowo
Przetwarzanie wstępne — preprocessing
...
...
...
...
...
...
...
...
..
..
...
.
..
..
..
.
.
..
..
..
..
.
..
..
..
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
..
...
...
.
..
..
..
.
.
..
..
..
..
.
..
..
..
....
...
...
...
..
...
...
...
...
...
.
...
...
...
#include "plik"
...
...
...
pl
ik
#include — program na wielu plikach
Włącza (bez interpretowania) cały plik.
Dyrektywa #include"plik" włącza plik z aktualnego katalogu — opracowany przez programistę.
Dyrektywa #include<plik> włącza plik z katalogu kompilatora — opracowany przez autora kompilatora.
Wykład 7. Przetwarzanie wstępne, str. 4
Przetwarzanie wstępne — preprocessing
#include — program na wielu plikach
#include "globalne"
int main ( ) {
printf("\n %s\n\n", haslo);
return 0;
}
globalne
#include<stdio.h>
char haslo[20] = "Naprzod!";
Drukuje:
Naprzod!
Plik „inkludowany”
może „inkludować”
inne pliki.
Przetwarzanie wstępne — preprocessing
#include — program na wielu plikach
Co inkludujemy, pisząc na początku programu
#include<stdio.h> lub #include<math.h> itp.?
(Na sigmie te pliki mieszczą w /usr/include/ — można zajrzeć.)
W standardowym pliku nagłówkowym (header file) mieszczą się m.in.
• definicje typów — np. FILE
• definicje stałych — np. EOF
• deklaracje zmiennych — np. stdout (zmienna typu FILE*, wskaźnik
na plik związany z ekranem)
• nagłówki funkcji — np. printf; wyłącznie nagłówki, bez definicji!
Np.
int printf(char* format, ...);
Definicje funkcji standardowych mieszczą się w bibliotece standardowej C,
a nie w pliku nagłówkowym.
Wykład 7. Przetwarzanie wstępne, str. 6
Przetwarzanie wstępne — preprocessing
#include — własny plik nagłówkowy
Modularność programu ułatwia panowanie nad dużymi systemami. W najprostszym przypadku:
• funkcje ogólne na jednym pliku; np. 1.ulamki-definicje.c
• używający ich program główny na innym;
np. 1.ulamki-glowny.c
• wspólna kompilacja:
gcc -Wall 1.ulamki-definicje.c 1.ulamki-glowny.c
(patrz 07-Ulamki/ ).
gcc -Wall
część wspólna
część wspólna
definicje
główny
Przetwarzanie wstępne — preprocessing
#include — własny plik nagłówkowy
część wspólna
gcc -Wall
•
•
•
•
#include
.. .h
.................... .................................
..........
.............
.
.
.
.
.
.
.
.
.......
......
.
.
.....
.
.
..
...
....
...
...
...
.
❄
.
" "
#include " ..❄
"
definicje
główny
typy, stałe, zmienne i nagłówki funkcji; np. 2.ulamki.h ;
funkcje ogólne; np. 2.ulamki-definicje.c ;
używający ich program główny; np. 2.ulamki-glowny.c ;
wspólna kompilacja.
)
biblioteka
Wykład 7. Przetwarzanie wstępne, str. 8
Przetwarzanie wstępne — preprocessing
#include — własny plik nagłówkowy
• Utrzymywanie tej samej części wspólnej w kilku różnych plikach jest
niepraktyczne. Dokonywanie w niej zmian wymaga zmieniania kilku
plików.
• Staramy się, żeby ten sam kod pisany był tylko raz; albo w pliku z
definicjami, albo w programie głównym.
• Minimum tego, co musi być znane zarówno definicjom jak programowi
głównemu (definicje typów, stałych, deklaracje zmiennych, nagłówki
funkcji) wynosimy do odrębnego pliku nagłówkowego.
• Nazwy plików nagłówkowych tradycyjnie mają rozszerzenie .h od
ang. header .
• Autor programu głównego nie musi znać treści pliku z definicjami, ale
powinien znać treść pliku nagłówkowego.
Przetwarzanie wstępne — preprocessing
#define — zastępowanie w programie
...
...
...
...
...
...
...
...
..
..
...
.
.
..
..
..
.
.
..
..
..
.
..
..
..
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
..
..
...
.
.
..
..
..
.
.
..
..
..
.
..
..
..
...
...
..
....
...
...
...
...
...
...
...
#define ABC defg
...
ABC
...
ABC
...
...
...
...
...
...
...
...
...
..
..
...
.
.
..
..
..
.
.
..
..
..
.
..
..
..
...
...
...
...
...
...
...
...
...
...
...
...
...
defg
...
defg
...
...
...
...
...
...
...
...
...
..
..
...
..
..
..
..
.
.
..
..
..
.
..
..
..
...
...
...
...
...
...
...
...
...
...
Dyrektywa
#define MAKRO reszta wiersza
powoduje zastąpienie wszystkich wystąpień MAKRa w programie przez
resztę wiersza.
Wykład 7. Przetwarzanie wstępne, str. 10
Przetwarzanie wstępne — preprocessing
#define — zastępowanie w programie
Przykład: (UWAŻAĆ!)
M
#define ILE 100;
···
if (ILE < 1000) printf("\n mniejsze\n\n");
else printf("\n niemniejsze\n\n");
sygnalizuje błąd:
error: expected ’)’ before ’;’ token
Wynik zastąpienia:
if (100; < 1000) printf("\n mniejsze\n\n");
else printf("\n niemniejsze\n\n");
Winny jest średnik.
Przetwarzanie wstępne — preprocessing
#define — zastępowanie w programie
• Nie zastępuje się części słów;
np. nie będzie żadnego zastąpienia w tym przypadku:
#define KOT pies
...............
... BOJKOT ...
• Nie zastępuje się we wnętrzu cudzysłowu;
np. nie będzie żadnego zastąpienia w tym przypadku:
#define KOT pies
...............
printf("Wlazł KOT na płot.\n");
Wykład 7. Przetwarzanie wstępne, str. 12
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
Makro może mieć parametry
— nazwy pooddzielane przecinkami.
Przy zastępowaniu parametry
są tekstowo (czyli na ślepo, „bezmyślnie”) zastępowane argumentami wywołania.
#define KWADRAT(x) x*x
........................
printf(" %i\n", KWADRAT(4));
printf(" %.2lf\n", KWADRAT(1.2));
printf(" %i\n", 4*4);
printf(" %.2lf\n", 1.2*1.2);
Ale trzeba uważać. . .
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
#define KWADRAT(x) x*x
........................
printf(" %i\n", KWADRAT(2+2));
Taka komenda drukuje 8
(zamiast 16 ).
Wynik zastąpienia: 2+2*2+2
Lepsza definicja:
#define KWADRAT(x) (x)*(x)
wynik zastąpienia:
(2+2)*(2+2)
Wykład 7. Przetwarzanie wstępne, str. 14
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
#define DLA(I, N) for(I=0; I<(N); I++)
— pętla po I od 0 do N
Wtedy:
w=1;
w=1;
DLA(i,N) w = 2*w;
DLA(i,N) w = w*(i+1);
DLA(i,n) DLA(j,k) tab[i][j] = 0;
— oblicza potęgę w = 2N
— oblicza silnię w = N !
— zeruje tablicę tab[n][k]
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
UWAGA NA SPACJE!
#define ZLE (int)0.6+(int)
.....................
printf("%i\n", (int)(ZLE(0.6)));
#define ZLE(int)0.6+(int)
.....................
printf("%i\n", (int)(ZLE(0.6)));
makro bez parametrów:
printf("%i\n", (int)((int)0.6+(int)(0.6)));
drukuje:
0
makro z parametrem:
printf("%i\n", (int)(0.6+0.6));
drukuje:
1
Wykład 7. Przetwarzanie wstępne, str. 16
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
Poprzedzenie parametru makra płotkiem # powoduje ujęcie argumentu w
cudzysłów:
#define PYTAJ O(zmienna) \
{ printf(" %s == ", #zmienna); scanf("%i", &zmienna); }
Wtedy PYTAJ O(N) rozwija się do
{ printf(" %s == ", "N"); scanf("%i", &N); }
i drukuje N == a następnie wczytuje liczbę całkowitą do zmiennej N .
Przetwarzanie wstępne — preprocessing
#define — makra standardowe
Komenda:
printf(" Data: %s. Czas: %s. Plik: %s. Wiersz: %i.\n",
DATE , TIME , FILE , LINE
);
wydrukowała:
Data: Mar 31 2013. Czas: 14:43:33. Plik: test.c. Wiersz: 5.
DATE
TIME
FILE
LINE
—
—
—
—
data
czas (godz.:min.:sek.)
przetwarzany plik
nr wiersza
Wykład 7. Przetwarzanie wstępne, str. 18
Przetwarzanie wstępne — preprocessing
#define — zastosowanie makr
FILE
i
#define BLAD WEWN\
printf("\n BLAD WEWNETRZNY.");\
printf("\n Powiadom autora programu podajac:");\
printf("\n
nazwe pliku: %s", FILE );\
printf("\n
numer wiersza:%i\n\n", LINE );
.....................
if (...) BLAD WEWN
.....................
drukuje:
BLAD WEWNETRZNY.
Powiadom autora programu podajac:
nazwe pliku: test.c
numer wiersza: 10
LINE
Przetwarzanie wstępne — preprocessing
#define — zastępowanie z parametrami
Napisy, ujęte w cudzysłowy i postawione obok siebie, zlewają się w jeden:
char napis[30] = "Maly " "dom.";
=
char napis[30] ="Maly dom.";
#define OSTRZEGAJ JESLI(cos zle) \
if (cos zle)\
printf ("OSTRZEZENIE: " #cos zle " w wierszu %i\n",
..............................
OSTRZEGAJ JESLI(a[i]==0)
LINE );
if (a[i]==0)
printf ("OSTRZEZENIE: " "a[i]==0" " w wierszu %i\n", 10);
drukuje:
OSTRZEZENIE: a[i]==0 w wierszu 10
Wykład 7. Przetwarzanie wstępne, str. 20
Przetwarzanie wstępne — preprocessing
Dziedziczenie wielostronne
W wyniku
wielostronnego
dziedziczenia przez
#include dojść
może do
wielokrotnej definicji
tego samego typu.
Błąd:
conflicting types
for ’punkt’
Jak tego uniknąć?
plik punkt.h :
typedef struct { double x,y; } punkt;
plik odcinek.h :
plik trojkat.h :
#include "punkt.h"
typedef struct {
punkt pocz,kon;
} odcinek;
#include "punkt.h"
typedef struct {
punkt w1,w2,w3;
} trojkat;
plik geom.h :
#include "odcinek.h"
#include "trojkat.h"
......
trojkat tr = {{0,0}, {1,0}, {0,1}};
......
Przetwarzanie wstępne — preprocessing
Dziedziczenie wielostronne
W wyniku
wielostronnego
dziedziczenia przez
#include dojść
może do
wielokrotnej definicji
tego samego typu.
Błąd:
conflicting types
for ’punkt’
Jak tego uniknąć?
plik punkt.h :
#ifndef PUNKT
#define PUNKT
typedef struct { double x,y; } punkt;
#endif
#define PUNKT definiuje makro specyficzne dla
pliku punkt.h .
Część programu między #ifndef PUNKT a
#endif jest wykonywana tylko wtedy, gdy makro
PUNKT jest niezdefiniowane. To się może zdarzyć
tylko jeden raz.
Wobec tego typ punkt zostanie zdefiniowany
tylko raz.
Wykład 7. Przetwarzanie wstępne, str. 22
Przetwarzanie wstępne — preprocessing
Kompilacja warunkowa —
dyrektywy #if, #else, #elif, #endif
poprzednio omawiana konstrukcja #ifndef...#endif
też jest przykładem kompilacji warunkowej
Te dyrektywy włączają do kompilacji fragmenty programu np. w zależności
od
• używanej maszyny lub systemu operacyjnego lub kompilatora,
• stadium uruchamiania programu — wydruki testowe, ostatecznie usuwane,
• chwilowego wyboru wersji,
• itp.
Przetwarzanie wstępne — preprocessing
Kompilacja warunkowa —
dyrektywy #if, #else, #elif, #endif
#if warunek0 program0
#elif warunek1 program1
#elif warunek2 program2
........................
#elif warunekn programn
#else programn+1
#endif
jeśli spełniony jest warunek0 ,
to kompilowany jest tylko program0
jeśli spełniony jest warunek1 ,
to kompilowany jest tylko program1
. . . itd.. . .
jeśli nie jest spełniony żaden z warunków,
to kompilowany jest tylko programn+1
Niekompilowanie niepotrzebnego kodu daje oszczędność pamięci.
Wykład 7. Przetwarzanie wstępne, str. 24
Przetwarzanie wstępne — preprocessing
Kompilacja warunkowa —
dyrektywy #if, #else, #elif, #endif
Przykład:
M
(Patrz 07-Sortowanie/)
W pliku sort.h można
wybrać jeden z algorytmów:
#define BUBBLE
// #define INSERTION
// #define MERGE
W pliku sort.c temu wyborowi odpowiada kompilacja warunkowa:
#if defined BUBBLE
void sort(double tab[WLK TABLICY]) {
. . . sortowanie bąbelkowe. . .
}
#elif defined INSERTION
void sort(double tab[WLK TABLICY]) {
. . . sortowanie przez wstawianie. . .
}
#elif defined MERGE
void sort(double tab[WLK TABLICY]) {
. . . sortowanie przez scalanie. . .
}
#else
#error "Nieznany algorytm sortowania."
#endif
Przetwarzanie wstępne — preprocessing
Jak automatycznie uzyskać wynik preprocessingu?
Komenda
gcc -E -CC plik z dyrektywami
dokonuje przetwarzania wstępnego i niczego więcej; i drukuje tekst z rozwiniętymi dyrektywami:
#define
#define
#define
#define
mleka piwa
tego ten
szczescie zly los
czeka wykiwa
Kto wypije mleka,
tego szczescie czeka.
Kto wypije piwa,
ten zly los wykiwa.

Podobne dokumenty