przegląd

Transkrypt

przegląd
Zmienne globalne i lokalne
• Zmienna globalna zadeklarowana poza wszystkimi funkcjami, jest
rozpoznawana od miejsca deklaracji do końca pliku (programu) —
poza miejscami, gdzie jest zasłonięta parametrami funkcji oraz deklaracjami lokalnymi.
• Parametr funkcji zadeklarowany w jej nagłówku oraz zmienna lokalna
funkcji zadeklarowana w jej ciele, jest rozpoznawana od miejsca deklaracji do nawiasu klamrowego zamykającego najmniejszy blok {. . . } ,
w którym mieści się deklaracja — poza miejscami, gdzie jest zasłonięta
deklaracjami lokalnymi z mniejszych bloków.
Wykład 8. Budowanie aplikacji, str. 2
Zmienne globalne i lokalne
typ zmienna 0;
typ zmienna 1;
typ zmienna 2;
globalna
lokalna
globalna
lokalna lokalna
typ funkcja (typ parametr) {
typ zmienna 0;
..................
. . . zmienna 0. . .
. . . zmienna 1. . .
. . . zmienna 3. . .
{ typ zmienna 2;
. . . zmienna 0. . .
. . . zmienna 1. . .
. . . zmienna 2. . .
}
}
niezadeklarowana
Zmienna użyta w programie należy do deklaracji z najmniejszego bloku
(ograniczonego nawiasami klamrowymi {...}), w jakim się znajduje.
Zmienne globalne i lokalne
#include<stdio.h>
W zasadzie. . .
Jeśli obliczenie programu opuści
blok, w którym zmienna jest rozpoznawana, to wartość zmiennej przestaje istnieć. Po następnym wejściu do tego bloku (i
ponownym przejściu przez deklarację) wartość zmiennej jest nieokreślona lub wynikająca z inicjalizacji.
void qq ( ) {
int a = 0;
printf(" a == %i", a); a++;
}
int main ( ) {
while (1) {
qq( ); getchar( );
}
return 0;
}
Wydruki:
a == 0
a == 0
a == 0
......
Wykład 8. Budowanie aplikacji, str. 4
Zmienne globalne i lokalne
W zasadzie. . .
Jeśli obliczenie programu opuści
blok, w którym zmienna jest rozpoznawana, to wartość zmiennej przestaje istnieć. Po następnym wejściu do tego bloku (i
ponownym przejściu przez deklarację) wartość zmiennej jest nieokreślona lub wynikająca z inicjalizacji.
#include<stdio.h>
int main ( ) {
int po raz 1 = 1;
while (1) {
{ int a[1];
if (po raz 1) a[0]=1;
printf(" a[0] == %i", a[0]);
getchar( ); po raz 1 = 0;
}
{ int b[1]; b[0]=2;
printf(" b[0] == %i\n", b[0]);
}
}
Wydruki:
return 0;
a[0] == 1
}
b[0] == 2
a[0] == 2
b[0] == 2
a[0] == 2
......
Zmienne globalne i lokalne
W zasadzie. . .
Jeśli obliczenie programu opuści
blok, w którym zmienna jest rozpoznawana, to wartość zmiennej przestaje istnieć. Po następnym wejściu do tego bloku (i
ponownym przejściu przez deklarację) wartość zmiennej jest nieokreślona lub wynikająca z inicjalizacji.
To można zmienić przez poprzedzenie deklaracji kwalifikatorem static.
Zmienna statyczna przechowuje wartość poprzez swoje życie
pozagrobowe aż do następnej
reinkarnacji.
Wykład 8. Budowanie aplikacji, str. 6
Zmienne globalne i lokalne
#include<stdio.h>
#include<stdio.h>
void qq ( ) {
int a = 0;
printf(" a == %i", a); a++;
}
void qq ( ) {
static int a = 0;
printf(" a == %i", a); a++;
}
int main ( ) {
while (1) {
qq( ); getchar( );
}
return 0;
}
int main ( ) {
while (1) {
qq( ); getchar( );
}
return 0;
}
Wydruki:
a == 0
a == 0
a == 0
a == 0
.........
Wydruki:
a == 0
a == 1
a == 2
a == 3
.........
Zmienne globalne i lokalne
W programie na jednym pliku
• Ta sama nazwa w programie może zależnie od kontekstu oznaczać
różne zmienne z różnych deklaracji.
• Zmienna należy do deklaracji z najmniejszego bloku (ograniczonego
nawiasami klamrowymi {...}), w jakim się znajduje.
• Deklaracje z większego bloku są zasłaniane deklaracjami tych samych
zmiennych w jego podblokach.
•
A jak jest z programem na wielu plikach?
Wykład 8. Budowanie aplikacji, str. 8
Zmienne globalne i lokalne
W programie na wielu plikach — skomplikowane. . .
• Każda zmienna musi mieć deklarację w pliku, w którym występuje. Brak deklaracji we
własnym pliku jest błędem.
• Deklaracje tej samej nazwy w różnych plikach na najwyższym poziomie (czyli poza wszelkimi funkcjami) oznaczają tą samą
zmienną.
• Nazwa zadeklarowana w jednym pliku na
najwyższym poziomie, a w innym wewnątrz
funkcji, oznacza różne zmienne (zasłanianie).
• Można to zmienić przy pomocy kwalifikatora extern .
WSPÓŁPRACA
JEST TRUDNA
Deklar. w różnych plikach na najwyższym poziomie
pomocniczy.c
#include<stdio.h>
glowny.c
#include<stdio.h>
int a;
int a;
void zmiana ( ) {
a++;
printf(" pom: a == %i\n", a);
}
void zmiana( );
Wydruki:
glow: a == 0
pom: a == 1
glow: a == 1
pom: a == 3
int main ( ) {
a=0;
printf(" glow: a == %i\n", a);
zmiana( );
printf(" glow: a == %i\n", a);
a++;
zmiana( );
return 0;
}
Wykład 8. Budowanie aplikacji, str. 10
Deklar. w różnych plikach na różnych poziomach
pomocniczy.h
#include<stdio.h>
void nowa wartosc( );
pomocniczy.c
#include"pomocniczy.h"
glowny.c
#include"pomocniczy.h"
int wart=0;
void nowa wart( ) {
printf(" pom: %i\n", wart);
wart++;
}
int main( ) {
int wart;
wart=10;
printf(" glow: %i\n", wart);
wart++;
nowa wart( );
printf(" glow: %i\n", wart);
wart++;
nowa wart( );
return 0;
}
Wydruki:
główny
pomocniczy
10
11
0
1
DWIE RÓŻNE ZMIENNE wart
Użycie extern
pomocniczy.h
#include<stdio.h>
void nowa wartosc( );
pomocniczy.c
#include"pomocniczy.h"
glowny.c
#include"pomocniczy.h"
int wart=0;
int main( ) {
extern int wart;
wart=10;
printf(" glow: %i\n", wart);
wart++;
nowa wart( );
printf(" glow: %i\n", wart);
wart++;
nowa wart( );
return 0;
}
void nowa wart( ) {
printf(" pom: %i\n", wart);
wart++;
}
Wydruki:
główny
pomocniczy
10
12
11
13
TA SAMA ZMIENNA wart Z PLIKU pomocniczy.c
Wykład 8. Budowanie aplikacji, str. 12
Ucząca się gra w zapałki
REGUŁY GRY:
1. Początkowo na stole leży n zapałek.
2. Gracze kolejno zabierają po 1, lub 2, lub 3 zapałki.
3. Kto weźmie ostatnią zapałkę, ten przegrywa.
ZAŁOŻENIA PROGRAMU:
• Człowiek gra z komputerem.
• Komputer początkowo nie ma żadnej taktyki gry; zabiera przypadkowo
po 1, lub 2, lub 3 zapałki. Nawet nie wie, o co toczy się gra (nie zna
reguły 3).
• Po każdej rozgrywce dostaje odpowiedź, czy wygrał, czy przegrał. W
następnych rozgrywkach dąży do wykonywania ruchów wygrywających,
a unikania przegrywających.
• Dla przyspieszenia nauki, rozgrywkę z człowiekiem poprzedzamy treningowymi rozgrywkami komputera samego ze sobą.
Ucząca się gra w zapałki
Trening:
W dwóch różnych plikach zapisujemy (identyczne) programy
• komp 0.c — z którym chcemy grać; oraz
• komp 1.c — sparring-partnera dla komp 0.c .
Każdy z nich uczy się niezależnie i nie dzieli się wiedzą z partnerem.
Po każdej rozgrywce osobny program arbiter.c informuje oba programy,
który z nich wygrał.
Gra z człowiekiem:
Po treningach gramy z programem komp 0.c .
Wykład 8. Budowanie aplikacji, str. 14
Ucząca się gra w zapałki
Nauka:
Obaj komputerowi gracze zapamiętują swoje ruchy, a po zakończonej rozgrywce dowiadują się, czy skutkiem był sukces czy porażka. Wtedy uaktualniają sobie tabele z punktacją ruchów:
punkty:
ile
zapałek
1
2
···
k
···
n
1
2
3
Ile warte jest zabranie 2 zapałek,
gdy przede mną leży ich k :
···
···
···
✾
···
···
···
liczba sukcesów − liczba porażek
z gier, w których tak zrobiłem.
Ucząca się gra w zapałki
Wybór ruchu:
Jeśli przeciwnik pozostawił k zapałek, to należy zabrać takie ℓ zapałek, żeby
liczba
punkty[k][ℓ]
była jak największa.
Kompilacja:
gcc -Wall -std=c99 *.c -o gra
Wywołanie:
./gra ile zapalek ile treningow
PROGRAM MIEŚCI SIĘ W KATALOGU
08-GraWZapalki
Wykład 8. Budowanie aplikacji, str. 16
Ucząca się gra w zapałki
Struktura plików w programie:
<stdio.h>
<stdlib.h>
"wspolne.h"
komp 0.c
komp 1.c
arbiter.c
gra.c
Plik wspolne.h :
#define MAX ZAPALEK ...
typedef struct {...} dwuciag;
Plik arbiter.c :
int legalny ruch (int ile zapalek, int branie);
int wygral (int ile zapalek, int ile ruchow);
Ucząca się gra w zapałki
Struktura plików w programie:
<stdio.h>
<stdlib.h>
"wspolne.h"
komp 0.c
komp 1.c
arbiter.c
gra.c
Plik komp 0.c :
void inic punktow 0 (void);
int ruch kompa 0 (int ile zapalek);
void nauka 0 (int wygrana);
void wydruk nauki 0 (void);
Plik komp 1.c :
tak samo
Wykład 8. Budowanie aplikacji, str. 18
Ucząca się gra w zapałki
W plikach komp 0.c i komp 1.c
• spisy wykonanych ruchów oraz
• tablice z punktami
muszą być zapamiętywane w tablicach statycznych:
static int punkty 0[MAX ZAPALEK+1][3];
To powoduje, że
• każda z nich jest lokalna w swoim pliku, oraz
• ich wartości nie zostają zapomniane w trakcie działania innych części
programu.
Od programu źródłowego do wykonywalnego
• Każdy program musi zostać skompilowany (compiled) i skonsolidowany (linked) do programu wykonywalnego.
kompilacja z konsolidacją
............................................................
............................................gcc
......................
.......................
.
.
..................
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
...............
.
.
.
.
.
.
.
.
.
❥...
........
... .c
źródło
kompilacja
gcc -c
✲ ... .o
obiekt
konsolidacja
gcc
✲
...
wykonywalny
• Oba etapy mogą zostać wykonane pojedynczym wywołaniem gcc.
• Można też je wykonać jeden po drugim:
– gcc -c — żeby uzyskać obiekt;
– gcc — żeby z pliku źródłowego lub obiektu uzyskać kod wykonywalny.
Wykład 8. Budowanie aplikacji, str. 20
Od programu źródłowego do wykonywalnego
• Program na wielu plikach również musi zostać skompilowany i skonsolidowany do programu wykonywalnego.
kompilacja z konsolidacją
... .c
źródło
... .c
źródło
...................................
.........................................................gcc
....................
.....................
.
.
.
.
.
................
.
.
.
.
.
.
.
.
.
.
..............
.........

............
...........

...........
kompilacja ✲ ... .o 
❥...


gcc -c
kompilacja
gcc -c
obiekt
✲ ... .o
obiekt










...
konsolidacja ✲
gcc
wykonywalny
• Kompilowany jest każdy plik z osobna.
• Konsolidowane są wszystkie pliki obiektowe razem.
Kiedy kompilacja jest długa i kosztowna. . .
xxx.h
.........
aaa.c
#include "xxx.h"
.........
yyy.h
.........
ccc.c
#include "xxx.h"
#include "yyy.h"
.........
bbb.c
#include "yyy.h"
.........
abc
gcc -Wall -std=c99 *.c
Ta komenda dokona kompilacji i konsolidacji wszystkich plików.
Jeśli trzeba wprowadzić poprawkę, to znowu trzeba skompilować i skonsolidować wszystkie pliki.
Wykład 8. Budowanie aplikacji, str. 22
Kiedy kompilacja jest długa i kosztowna. . .
xxx.h
.........
yyy.h
.........
aaa.c
#include "xxx.h"
.........
ccc.c
#include "xxx.h"
#include "yyy.h"
.........
bbb.c
#include "yyy.h"
.........
aaa.o
ccc.o
bbb.o
abc
gcc -c -Wall -std=c99 *.c
gcc *.o
Te komendy dokonają kompilacji od źródeł do obiektów, a następnie konsolidacji.
Kiedy kompilacja jest długa i kosztowna. . .
xxx.h
.........
yyy.h
.........
aaa.c
#include "xxx.h"
.........
ccc.c
#include "xxx.h"
#include "yyy.h"
.........
bbb.c
#include "yyy.h"
.........
aaa.o
ccc.o
bbb.o
abc
gcc -c -Wall -std=c99 aaa.c
gcc *.o
Zmiana jednego ze źródeł wymaga kompilacji tylko tego źródła oraz
konsolidacji.
Wykład 8. Budowanie aplikacji, str. 24
Kiedy kompilacja jest długa i kosztowna. . .
xxx.h
.........
yyy.h
.........
aaa.c
#include "xxx.h"
.........
ccc.c
#include "xxx.h"
#include "yyy.h"
.........
bbb.c
#include "yyy.h"
.........
aaa.o
ccc.o
bbb.o
abc
gcc -c -Wall -std=c99 aaa.c
gcc -c -Wall -std=c99 ccc.c
gcc *.o
Zmiana w pliku nagłówkowym — rekompilacja plików, które go inkludują.
Kiedy kompilacja jest długa i kosztowna. . .
• Jeśli dysponujemy w katalogu plikami obiektowymi ... .o , to zmiana
niektórych plików źródłowych ... .c wymaga ponownej kompilacji
tylko zmienionych plików; oraz konsolidacji całości.
• Pomyłka w tym, które pliki rekompilujemy, powoduje błąd trudny do
znalezienia.
• Specjalne narzędzie make , towarzyszące kompilatorowi gcc , dba o
to, żeby rekompilowane były tylko potrzebne pliki.
Dystrybucja oprogramowania
Ze względu na różnorodność komputerów, systemów operacyjnych, konfiguracji sprzętu i oprogramowania rozpowszechnianie nowych programów w gotowej postaci wykonywalnej jest niepraktyczne. Rozpowszechnia się źródła
oraz „ściągę”, wg której program make dokonuje potrzebnych kompilacji,
konsolidacji i innych czynności.
Wykład 8. Budowanie aplikacji, str. 26
make i Makefile
0
1
2
3
4
5
6
7
8
9
10
Makefile
wszystko: pl1 pl2 pl3
cat pl1 pl2 pl3 > calosc
ls -l --time=ctime
pl1: p1
tr ’Oo’ ’Aa’ < p1 > pl1
pl2:
echo -n " ma" > pl2
pl3:
echo -n " kota." > pl3
wyczysc:
rm pl1 pl2 pl3 calosc *
Wiersz 0: cel wszystko zależy od celów pl1, pl2 i pl3.
Wiersze 1 i 2: co trzeba zrobić dla realizacji celu wszystko.
Wiersz 3: cel pl1 zależy od celu p1.
Wiersz 4: co trzeba zrobić dla realizacji celu pl1.
Wiersze 5, 7 i 9: cele pl2, pl3 i
wyczysc nie zależą od innych.
Wiersze 6, 8 i 10: co trzeba zrobić dla
ich realizacji.
Program make wykonuje komendy zapisane w pliku Makefile znajdującym się w aktualnym katalogu.
W pliku Makefile zapisane są cele do realizacji, zależności od innych
celów, komendy bash -a.
make i Makefile
0
1
2
3
4
5
6
7
8
9
10
Makefile
wszystko: pl1 pl2 pl3
cat pl1 pl2 pl3 > calosc
ls -l --time=ctime
pl1: p1
tr ’Oo’ ’Aa’ < p1 > pl1
pl2:
echo -n " ma" > pl2
pl3:
echo -n " kota." > pl3
wyczysc:
rm pl1 pl2 pl3 calosc *
DRZEWO ZALEŻNOŚCI
wszystko
pl1 pl2 pl3
p1
wyczysc
„Cel A zależy od celu B” oznacza, że dla realizacji celu A trzeba sprawdzić,
czy w międzyczasie nie uległo zmianie coś, od czego zależą wyniki B i jeśli
tak, to powtórzyć realizację B.
Wykład 8. Budowanie aplikacji, str. 28
Typowy Makefile dla programów w C
xxx.h
.........
yyy.h
.........
ccc.c
aaa.c
bbb.c
#include "xxx.h"
#include "xxx.h"
#include "yyy.h"
#include "yyy.h"
.........
.........
.........
Makefile
OBJ = aaa.o bbb.o ccc.o
aaa.o
ccc.o
bbb.o
abc
Komend gcc -c ,
kompilujących plik.c do plik.o ,
oraz zależności plik.o od plik.c
nie trzeba pisać, bo są domyślne.
prog: $(OBJ)
gcc -o abc $(OBJ)
aaa.o: xxx.h
bbb.o: yyy.h
ccc.o: xxx.h yyy.h
clean :
rm abc $(OBJ)
Typowy Makefile dla programów w C
• Jeśli program jest rozbity na kilka plików, to warto jawnie rozdzielić
kompilację i konsolidację programu na
– etap kompilacji — komenda gcc -c ; oraz
– etap konsolidacji — komenda gcc .
• W tym celu należy sporządzić pomocniczy plik Makefile . Potem
po każdej zmianie w którymkolwiek pliku źródłowym komenda make
uaktualni przekład, nie wykonując niepotrzebnych kompilacji.
• Język zastosowany w konstruowaniu pliku Makefile zawiera w sobie
makrogenerator podobny do mechanizmu #define w preprocesorze C.
• Język zastosowany w konstruowaniu pliku Makefile to już czwarty
język programowania poznany przez Państwo, po C, preprocesorze C
oraz języku powłoki bash .