Wyklad PI 9


Programowanie w języku C
Kurs początkowy
Dr inż. Lucjan Miękina
upel.agh.edu.pl/wimir/login/
Katedra Robotyki i Mechatroniki
December 3, 2012
1/1
Programowanie w języku C
Struktury - struktury jako elementy tablic
Przykład podobny do poprzedniego, ale z inicjalizacją desygnatorami:
1 #include
2 struct osoba {
3 char* imie;
4 char* nazwisko;
5 int wiek;
6 };
7 int main() {
8 struct osoba guitarists[4] = { // tablica 4 elementow
9 [0].imie = "John", [0].nazwisko = "Scofield",
10 [1].imie = "Pat", [1].nazwisko = "Metheny",
11 [2].imie = "John", [2].nazwisko = "McLaughlin" };
12 // podstawienie wartosci do niezainicjowanych pol
13 guitarists[0].wiek = 55;
14 guitarists[1].wiek = 50;
15 guitarists[2].wiek = 60;
16 guitarists[3].wiek = 0; // struktura jest nieuzywana
17 int i, ile = sizeof(guitarists) / sizeof(struct osoba);
18 printf("Tablica guitarists ma %d struktury, a w nich:\n", ile);
19 for(i = 0; i < ile; i++)
20 if(guitarists[i].wiek) // tylko gdy wiek jest różny do zera
21 printf("- %s %s, %d lat\n", guitarists[i].imie, guitarists[i].nazwisko, guitarists[i].wiek);
22 return 0;
23 }
Rezultat wykonania:
Tablica guitarists ma 4 struktury, a w nich:
- John Scofield, 55 lat
- Pat Metheny, 50 lat
- John McLaughlin, 60 lat
2/1
Programowanie w języku C
Struktury - implementacja list z pomocą struktur
Program ilustruje sposób
1 #include
obsługi listy struk-
2 #include
tur, realizowanej przez
3 struct osoba {
odpowiednie użycie pola
4 char* imie;
5 char* nazwisko; typu wskaznikowego.
6 int wiek;
Rezultat wykonania:
7 struct osoba *nast;
8 };
Elementy listy:
9 int main() {
- Lucjan M., 44
10 struct osoba *pStudenci = NULL, *pRob;
11 pRob = realloc(NULL, sizeof(struct osoba)); - Szymon M., 9
12 if(pRob) {
13 pRob->imie = "Lucjan"; pRob->nazwisko = "M.";
14 pRob->wiek = 44; pRob->nast = NULL;
15 pStudenci = pRob; // pamietaj poczatek !
16 }
17 // nastepny element
18 pRob->nast = realloc(NULL, sizeof(struct osoba));
19 if(pRob->nast) {
20 pRob = pRob->nast;
21 pRob->imie = "Szymon"; pRob->nazwisko = "M.";
22 pRob->wiek = 9; pRob->nast = NULL;
23 }
24 printf("Elementy listy:\n");
25 pRob = pStudenci;
26 while(pRob) {
27 printf("- %s %s, %d\n",
28 pRob->imie, pRob->nazwisko, pRob->wiek);
29 pRob = pRob->nast;
30 }
31 return 0;
32 }
3/1
Programowanie w języku C
Struktury - współpraca z funkcjami
Ponieważ operacje kopiowania i podstawienia są zdefiniowane dla struktur, można
przekazać strukturę do funkcji jako argument. Można również zwrócić strukturę z
funkcji.
1 #include
2 #include
3 struct osoba {
4 char* imie;
5 char* nazwisko;
6 int wiek;
7 };
8 struct osoba utworz(char* imie, char* nazwisko, int wiek) {
9 struct osoba tmp;
10 tmp.imie = imie;
11 tmp.nazwisko = nazwisko;
12 tmp.wiek = wiek;
13 return tmp;
14 }
15 int porownaj(struct osoba p1, struct osoba p2) {
16 int cmp = 0;
17 if(strcmp(p1.imie, p2.imie))
18 cmp++;
19 if(strcmp(p1.nazwisko, p2.nazwisko))
20 cmp++;
21 if(p1.wiek - p2.wiek)
22 cmp++;
23 return cmp;
24 }
4/1
Programowanie w języku C
Struktury - współpraca z funkcjami
25 int main() {
26 struct osoba p1 = utworz("p1.imie", "p1.nazwisko", 1);
27 struct osoba p2 = utworz("p2.imie", "p2.nazwisko", 2);
28 printf("p1 %p: %s %s, %d\n", &p1, p1.imie, p1.nazwisko, p1.wiek);
29 printf("p2 %p: %s %s, %d\n", &p2, p2.imie, p2.nazwisko, p2.wiek);
30 int roznice = porownaj(p1, p2);
31 if(roznice)
32 printf("Struktury rozne o %d pola\n", roznice);
33 else
34 printf("Struktury identyczne\n");
35 return 0;
36 }
Rezultat wykonania:
p1 0x7fff10eb5aa0: p1.imie p1.nazwisko, 1
p2 0x7fff10eb5a80: p2.imie p2.nazwisko, 2
Struktury rozne o 3 pola
UWAGA: jeśli należy przekazać dużą strukturę do funkcji, bardziej efektywne jest użycie wskaznika
do niej zamiast kopiowania całej struktury. W takim przypadku należy zadeklarować funkcję jako:
int porownaj(struct osoba *p1, struct osoba *p2);
a pierwsza instrukcja wyboru występująca w funkcji porownaj, powinna przyjąć postać:
if(strcmp(p1->imie, p2->imie))
cmp++;
5/1
Programowanie w języku C
Pola bitowe
Pola bitowe są używane, kiedy trzeba upakować wiele danych w jednym słowie
maszynowym. Na przykład, jeśli istnieją zewnętrznie zdefiniowane formaty danych
(najczęściej interfejsy do urządzeń), w których konkretne bity słowa sterującego lub
słowa stanu są przypisane do odpowiednich funkcji.
Tradycyjnie definiuje się w tym przypadku "maski" odpowiadające wyróżnionym bitom.
Na przykład, wybrane maski rejestru flag procesora Intel x86 mogą być zdefiniowane
jak poniżej:
#define FLAGA_PRZENIESIENIE 0x1
#define FLAGA_PARZYSTOSC 0x4
#define FLAGA_ZERO 0x40
#define FLAGA_ZNAK 0x80
#define FLAGA_PRZEPELNIENIE 0x800
lub za pomocą wyliczenia:
enum Flagi {
PRZENIESIENIE = 0x1, PARZYSTOSC = 0x4, ZERO = 0x40,
ZNAK = 0x80, PRZEPELNIENIE = 0x800
};
Wtedy dostęp do bitów słowa wymaga użycia operatorów bitowych (ang. bitwise
operators) o symbolach , ,|, &, .

To podejście jest nieco niewygodne i obciążone ryzykiem błędów, dlatego jako
alternatywę język C oferuje możliwość dostępu do części słowa bezpośrednio  przez
nazwę.
6/1
Programowanie w języku C
Pola bitowe
Pole bitowe
Pole bitowe jest zbiorem sąsiadujących bitów, należących do pojedynczego słowa
przechowywanego w pamięci.
Składnia definicji pola bitowego i dostępu do jego elementów jest oparta na
strukturach. Definicja pola zwykle zaczyna się od typu unsigned int (by była to
wartość bez znaku) , nazwy i dwukropka, za którym występuje liczba określająca
szerokość pola w bitach.
struct x86_flagi {
Używając pól bitowych, wybrane
unsigned int przeniesienie : 1;
maski rejestru flag procesora Intel
unsigned int wolne_1 : 1;
x86 mogą być zdefiniowane jak
unsigned int parzystosc : 1;
pokazano z lewej:
unsigned int wolne_2 : 3;
unsigned int zero : 1;
unsigned int znak : 1;
unsigned int wolne_3 : 3;
unsigned int przepelnienie : 1;
};
struct x86_flagi rej =
{ 0, 0, 0, 0, 0, 0, 0, 0 };
Rezultat wykonania:
printf("rej : %x\n", rej);
rej.przeniesienie = 1;
rej : 0
rej.parzystosc = 1;
printf("rej : %x\n", rej); rei : 5
7/1
Programowanie w języku C
Unie
Unia
Unia (ang. union) jest obiektem podobnym do struktury, z tym że wszystkie pola są
umieszczone w pamięci począwszy od tego samego adresu (nakładają się).
W związku z tym, unia może przechowywać w dowolnym momencie wartość tylko
jednego z tych pól. Unie dostarczają możliwości naprzemiennego przechowywania i
operowania na różnych danych w tym samym obszarze pamięci.
Domyślny inicjalizator dla unii mającej statyczne wiązanie (modułowej lub globalnej)
jest taki jak domyślny inicjalizator pierwszego jej pola; natomiast dla unii mającej
automatyczne wiązanie (lokalnej) domyślny inicjalizator nie jest zdefiniowany.
Pamięć rezerwowana dla unii jest taka, jak pamięć wymagana dla największego z jej
pól. Wszystkie składniki unii nakładają się na siebie w pamięci, ponieważ każde z pól
jest odwzorowane w pamięci począwszy od adresu początkowego unii jako całości.
Deklarowanie a definiowanie unii
Definicja typu unijnego opisuje składniki unii. Składa się ona ze słowa
kluczowego union, za którym opcjonalnie występuje nazwa (identyfikator unii), a
następnie w nawiasach klamrowych lista pól.
Deklaracja typu unijnego ma taką samą formę jak definicja, ale brak w niej
nawiasów klamrowych zawierających listę pól.
8/1
Programowanie w języku C
Unie
Oba aspekty ilustruje poniższy graf, w którym deklaracji odpowiada górna gałąz, a
definicji - dolna.
Identyfikator jest nazwą unii definiowanej łącznie z podaniem listy pól. Jeśli w
definicji podano jej nazwę, każda następna deklaracja unii w tym samym zakresie
może być wykonana z podaniem tej nazwy i opuszczeniem listy pól. Natomiast
jeśli nazwa nie została podana, wszystkie ewentualne zmienne odnoszące się do
tej unii muszą być zadeklarowane w dalszym ciągu instrukcji definiującej unię.
Lista pól zawiera opis obiektów, które mogą być przechowywane w unii (ich typ i
nazwę).
Definicja pola unii ma postać zgodną z deklaracją zmiennej.
Dostęp do pól unii odbywa się identycznie jak do pól struktur.
9/1
Programowanie w języku C
Unie - inicjalizacja
Dowolne pole unii może być zainicjalizowane, nie tylko pierwsze, za pomocą
desygnatora. Inicjalizator unii, który zawiera desygnatory, ma postać identyczną jak
dla struktury. Na przykład, definicja unii przydatnej do reprezentowania jednego z
typów standardowych języka C może wyglądać tak:
union typy_stand {
char c;
short s;
int i;
float f;
double d;
void* p;
} u = { .i = 13 };
Za definicją unii, zadeklarowano zmienną unijną u i zainicjowano pole i typu
całkowitego wartością 13 z użyciem desygnatora.
Wartość dowolnego typu standardowego może być przypisana do zmiennej u i pózniej
użyta w wyrażeniach, dopóki przestrzega się zasady: pole odczytywane jest to samo
co pole ostatnio zapisane. Za przestrzeganie tej zasady jest odpowiedzialny
programista, jako że język C nie zapewnia żadnej kontroli poprawności tego aspektu.
10/1
Programowanie w języku C
Unie - przykład
Każda definicja unii tworzy nowy typ, który nie jest tożsamy ani zgodny z żadnym
innym w tym samym zakresie kodu. Jednak deklaracja odnosząca się do już
zdefiniowanego typu jest z nim zgodna. Skojarzenie to jest oparte o nazwę unii.
1 #include
2 union typy_stand {
3 char c;
4 short s;
5 int i;
6 float f;
7 double d;
8 void* p;
9 };
10 int main() {
11 union typy_stand u = { .c =  A };
12 union typy_stand *pu = &u;
13 printf("Unia zawiera: %c\n", u.c);
14 printf("Unia (*) zawiera: %c\n", pu->c);
Rezultat wykonania:
15 u.p = "String";
16 printf("Unia zawiera: %s\n", (char*)u.p);
17 u.c =  A ; Unia zawiera: A
18 printf("Unia zawiera: %s\n", (char*)u.p);
Unia (*) zawiera: A
19 return 0;
Unia zawiera: String
20 }
Unia zawiera: rw^*red;
11/1
Programowanie w języku C
Deklaracja typedef
Deklaracja typedef pozwala zdefiniować własne identyfikatory typu, w miejsce typów
standardowych (jak int, float, czy double, itp.) lub innych. Nie powoduje ona
przydzielenia pamięci, ani nie powoduje utworzenia nowych typów danych, ale tworzy
synonimy dla już istniejących typów lub ich kombinacji. Także przestrzeń nazw dla
typów zdefiniowanych za pomocą deklaracji typedef jest taka sama jak dla innych
typów.
Kiedy obiekt (zwykle będący zmienną) jest zdefiniowany za pomocą typedef, jego
własności są takie same jakie by były, gdyby został zdefiniowany bez użycia deklaracji
typedef.
Przykłady
Poniżej zadeklarowano DLUGOSC jako synonim typu int, a następnie użyto
DLUGOSC w deklaracji zmiennych dlu, sze, i wys:
typedef int DLUGOSC;
DLUGOSC dlu, sze, wys;
co jest równoważne poniższym deklaracjom:
int dlu, sze, wys;
12/1
Programowanie w języku C
Deklaracja typedef
Podobnie, typedef może być użyty do definicji typu (wyliczeniowego, struktury, unii,
lub klasy w C++). Na przykład:
typedef struct {
char* imie;
char* nazwisko;
int wiek;
} osoba;
Następnie typ osoba może być użyty samodzielnie w deklaracjach zmiennych:
osoba p1, *p;
typedef i funkcje
Słowo typedef może być użyte do deklaracji funkcji lub wskazników do nich. W
poniższym przykładzie, typ PFICC jest zadeklarowany jako "wskaznik do funkcji z
dwoma parametrami typu char , która zwraca wartość typu int":
typedef int (*PFICC)(char*, char*);
Taki sposób może być użyty do deklaracji wskaznika do funkcji bibliotecznej strcmp w
następujący sposób:
PFICC strcmp;
co poprawia czytelność kodu.
13/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia
Dotychczas dyskutowane operacje wejścia/wyjścia realizowały odczyt danych ze
standardowego wejścia (ang. standard input) i zapisywały dane na standardowe
wyjście (ang. standard output); strumienie te są automatycznie tworzone dla
programu przez system operacyjny. Jednak rzadko jest to wystarczające  zwykle
zachodzi potrzeba współpracy z plikami indywidualnie dołączanymi do programu.
Zanim taki plik może być użyty przez program, musi on być otwarty przez funkcję
biblioteczną fopen. fopen na podstawie dostarczonych jej argumentów wywołania
(nazwa pliku i tryb otwarcia), zleca systemowi operacyjnemu otwarcie pliku, a
następnie zwraca wskaznik przydatny do operacji na tym pliku. Nazywany jest on
wskaznikiem plikowym (ang. file pointer) i zawiera adres struktury, przechowującej
dane o pliku (położenie jego bufora, bieżący bajt, zapis/odczyt, błędy lub napotkanie
końca pliku).
Typ wskaznika do pliku jest zdefiniowany w jako FILE, przy czym w
definicji użyto typedef, w związku z tym można typu FILE użyć bez słowa kluczowego
struct, jak w poniższym przykładzie:
FILE* fp; // niezainicjowany wsk. plikowy
FILE* fopen(char* nazwa, char* tryb); // deklaracja fopen
14/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - otwieranie pliku
Aby wywołać fopen w programie, należy napisać:
fp = fopen(nazwa, tryb);
gdzie nazwa jest łańcuchem znaków zawierającym nazwę pliku, a tryb jest łańcuchem
znaków, określającym sposób użycia pliku.
Dostępne tryby: odczyt ( r ), zapis ( w ), dołączanie ( a ), odczyt lub zapis od
początku pliku (r+), odczyt lub zapis z nadpisaniem (w+), odczyt lub zapis z
dopisaniem na końcu (a+).
Pliki tekstowe i binarne: dla binarnych znak  b musi wystąpić wewnątrz łańcucha
znaków trybu.
Reguły działania fopen:
Jeśli próbuje się otworzyć nieistniejący plik dla zapisu lub dołączania, jest on tworzony jeśli
jest to możliwe. Jeśli próbuje się otworzyć istniejący plik dla zapisu to jego zawartość
zostanie usunięta, natomiast otwarcie dla dołączania zachowuje dotychczasową zawartość
Jeśli próbuje się otworzyć nieistniejący plik dla odczytu to wystąpi błąd
Inne zródła błędów to: próba otwarcia pliku bez stosownych uprawnień, próba otwarcia pliku
już otwartego. Jeśli wystąpi jakikolwiek błąd, fopen zwraca wartość NULL. Dla lepszej
identyfikacji błędów można się posłużyć wyrażeniem errno (typu całkowitego), które zwraca
numer błędu, określający szczegóły ostatniego błędu jaki wystąpił w trakcie działania
programu.
15/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - zamykanie pliku
Po zakończeniu wykonywania operacji plikowych, należy zamknąć plik za pomocą
funkcji fclose, zadeklarowanej jako:
int fclose(FILE* fp);
która likwiduje powiązanie między wskaznikiem plikowym i plikiem, jednocześnie
usuwając z pamięci strukturę typu FILE i wszystkie bufory z tym plikiem związane.
Zaleca się niezwłoczne zamknięcie pliku po ostatniej operacji na nim, chociaż jeśli
program kończy się w normalny sposób, fclose jest wywoływane automatycznie dla
każdego otwartego pliku. Przemawiają za tym dwa powody:
System operacyjny posiada ograniczenie ilości równocześnie otwartych plików
fclose opróżnia bufor związany z plikiem, pozwalając wykonać fizyczny zapis do
pliku.
16/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - standardowe strumienie
W momencie uruchamiania programu w języku C, system operacyjny odpowiada za
otwarcie trzech plików i udostępnienie wskazników do nich. Te pliki to: standardowe
wejście (stdin), standardowe wyjście (stdout) i standardowe wyjście diagnostyczne
(stderr) . Odpowiadające im wskazniki plikowe (stdin, stdout i stderr) są
zadeklarowane w .
Normalnie stdin jest połączone z klawiaturą, stdout i stderr są połączone z ekranem,
ale stdin i stdout mogą być przekierowane do dowolnego pliku lub potoku.
Używanie standardowych strumieni
Dyskutowane wcześniej funkcje scanf i printf operują na standardowych strumieniach,
dostarczając możliwości formatowanego wprowadzania i wyprowadzania danych.
Istnieją prostsze funkcje, przeznaczone do transmisji jednego znaku za każdym
wywołaniem. Dla odczytu znaku ze stdin, można użyć funkcji:
int getchar(void);
która zwraca następny znak ze strumienia, lub EOF po napotkaniu końca pliku.
Dla zapisu znaku do stdout, można użyć funkcji:
int putchar(int);
która zapisuje znak będący jej argumentem do strumienia stdout i zwraca zapisany
znak, lub EOF po wystąpieniu błędu.
17/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - standardowe strumienie
Przykład użycia funkcji getchar i putchar. Program wykonuje pętlę while dopóki nie
napotka końca danych wejściowych (EOF); w każdym obiegu pętli wczytuje znak ze
stdin, następnie zamienia znak na wielką literę i zapisuje do stdout.
1 #include
2 #include
3
4 int main() {
5 int c;
6 while((c=getchar()) != EOF)
7 putchar(toupper(c));
8 return 0;
9 }
Rezultat wykonania:
male
MALE
mniejsze
MNIEJSZE
18/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - odczyt i zapis plików w trybie znakowym
Tryb znakowy jest najprostszą formą wejścia/wyjścia: pojedynczy znak jest
transmitowany między programem i plikiem.
Funkcja zadeklarowana:
int getc(FILE* fp);
zwraca następny znak z pliku identyfikowanego przez fp; w przypadku napotkania
końca pliku lub błędu zwraca EOF.
Funkcja zadeklarowana:
int putc(int c, FILE* fp);
jest używana do realizacji operacji wyjściowej, zapisuje ona znak c do pliku
identyfikowanego przez fp i zwraca zapisany znak lub EOF po wystąpieniu błędu.
Funkcje getchar i putchar mogą być zdefiniowane przez getc, putc, stdin i stdout
następująco:
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
19/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - odczyt i zapis plików w trybie znakowym
1 #include
2 #include
3
4 int main() {
5 int c;
6 FILE* fpi = fopen("i.txt", "rt");
7 FILE* fpo = fopen("o.txt", "wt");
8 if(fpi && fpo)
9 while((c=getc(fpi)) != EOF)
10 putc(toupper(c), fpo);
11 if(fpi)
12 fclose(fpi);
13 if(fpo)
14 fclose(fpo);
15 return 0;
16 }
Plik wejściowy (i.txt):
qwertyuiop
ASDFGHIJKL
Plik wyjściowy (o.txt):
QWERTYUIOP
ASDFGHIJKL
20/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - formatowane wyjście za pomocą funkcji fprintf
Funkcja fprintf jest identyczna jak printf, z wyjątkiem dodatkowego pierwszego
parametru, który jest wskaznikiem do struktury reprezentującej zapisywany plik.
Aańcuch formatujący jest drugim parametrem. Funkcja jest zadeklarowana jako:
int fprintf(FILE* fp, char* format, ...);
Wielokropek (...) oznacza, że za łańcuchem formatującym może wystąpić zmienna
ilość dodatkowych parametrów.
1 #include
2
3 int main() {
4 char* fname = "o.txt";
5 FILE* fp = fopen(fname, "wt");
6 if(fp) {
7 float tab[] = { 1., 2., 3.};
8 int i, len = sizeof(tab)/ sizeof(tab[0]);
9 fprintf(fp, "len=%2d\n", len);
Plik wyjściowy (o.txt):
10 for(i = 0; i < len; i++)
11 fprintf(fp, "tab[%2d]=%12.5e\n", i, tab[i]);
12 fclose(fp); len= 3
13 }
tab[ 0]= 1.00000e+00
14 return 0;
tab[ 1]= 2.00000e+00
15 }
tab[ 2]= 3.00000e+00
21/1
Programowanie w języku C
Plikowe operacje wejścia/wyjścia - formatowane wejście za pomocą funkcji fscanf
Funkcja fscanf jest identyczna jak scanf, z wyjątkiem dodatkowego pierwszego
parametru, który jest wskaznikiem do struktury reprezentującej odczytywany plik.
Aańcuch formatujący jest drugim parametrem. Funkcja jest zadeklarowana jako:
int fscanf(FILE* fp, char* format, ...);
Wielokropek (...)
oznacza, że za łańcuchem
1 #include
2 int main() { formatującym może
3 FILE* fp = fopen("o.txt", "rt");
wystąpić zmienna ilość
4 if(fp) {
dodatkowych
5 int len, ok = fscanf(fp, "len=%d\n", &len);
parametrów.
6 printf("%d pole: len=%d", ok, len);
7 if(ok > 0) {
8 int i, j;
9 float tab[len]; // zmienna jako ilosc el.!
10 for(i = 0; i < len; i++) { Rezultat wczytania pliku
11 ok = fscanf(fp, "tab[%d]", &j);
(o.txt):
12 printf("\nWczytano:");
13 if(ok) { // jesli wczytano indeks
1 pole: len=3
14 printf(" tab[%d]", j);
15 ok = fscanf(fp, "=%e\n", &tab[j]); Wczytano: tab[0]=1
16 if(ok) printf("=%g", tab[j]);
Wczytano: tab[1]=2
17 } } }
Wczytano: tab[2]=3
18 fclose(fp);
19 }
20 return 0;
21 }
22/1


Wyszukiwarka

Podobne podstrony:
Wyklad PI 5
Wyklad PI 1 cz 2
Wyklad PI
Wyklad PI 3
Wyklad PI 2 cz 2
Wyklad PI 4
Wyklad PI 8
Wyklad PI 2 cz 1
Wyklad PI
Sieci komputerowe wyklady dr Furtak
Wykład 05 Opadanie i fluidyzacja
WYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznej
mo3 wykladyJJ

więcej podobnych podstron