Ćwiczenie (1) Dostosuj poniższy program do wymogów programu zaliczeniowego #include typedef struct{ char imie[30]; char nazwisko[50]; int rokUrodzenia; char PESEL[20]; } klient; void zapisz(klient pDoZapisu) { FILE *plik; char a[100], sciezka[100] = "C:\\abc.txt" ; if ((plik = fopen(sciezka, "a")) == NULL) { printf("Nie mozna otworzyc %s\n", sciezka); system("PAUSE"); exit(1); } fprintf(plik, "PESEL:%s\n", pDoZapisu.PESEL); fprintf(plik, "Imie:%s\n", pDoZapisu.imie); fprintf(plik, "Nazwisko:%s\n", pDoZapisu.nazwisko); fprintf(plik, "Rok urodzenia:%d\n", pDoZapisu.rokUrodzenia); fclose(plik); }
Ćwiczenie (2) void dodaj(){ klient nowy; printf("Wprowadz PESEL:\n"); scanf("%s", &nowy.PESEL); printf("Wprowadz imie:\n"); scanf("%s", &nowy.imie); printf("Wprowadz nazwisko:\n"); scanf("%s", &nowy.nazwisko); printf("Wprowadz rok urodzenia:\n"); scanf("%d", &nowy.rokUrodzenia); zapisz(nowy); }; void edytuj(){}; void usun(){}; void wyszukaj(){}; void zakoncz(){};
Ćwiczenie (3) int main(int argc, char *argv[]){ int option; while(option!=0){ printf("Wybierz opcję:\n1 - Dodaj kienta\n2 - Edytuj klienta\n3 - Usuń klienta\n"); printf("4 - Wyszukaj klienta\n0 - Zakończ\n"); scanf("%d", &option); switch(option){ case 0: zakoncz(); break; case 1: dodaj(); break; case 2: edytuj(); break; case 3: usun(); break; case 4: wyszukaj(); break; } system("PAUSE"); return 0; }
Konwersje typów (1) Konwersje są dokonywane w momencie przypisywania zmiennej jednego typu do zmiennej innego typu. Polegają one na zmianie sposobu zapisu wartości. Niektóre konwersje dokonują się automatycznie, zaś inne wymagają jawnego określenia typów. Niejawne konwersje: 1)wywołanie funkcji, 2)zwracanie rezultatu funkcji, 3)w obecności operatorów, 4)wyrażenia, 5)wyrażenia inicjalizujące
Konwersje typów (2) Konwersja liczb całkowitych W miejscach użycia typu całkowitego można używać zmiennych typu char, short int, typu wyliczeniowy oraz pól bitowych. Kolejność wykonywania konwersji jawnych jest nieokreślona. Liczby całkowite ze znakiem są konwertowane tak, że ich zapis bitowy nie zmienia się. W przypadku jawnej konwersji typu bez znaku do typu ze znakiem wartość nie zmienia się, jeśli może być reprezentowana przez nowy typ.
Konwersje typów (3) Konwersja liczb zmiennoprzecinkowych Konwersje z typów o mniejszej precyzji do typów o większej precyzji pozostawiają nie zmienioną wartość. Konwersje z typów o większej precyzji do typów o mniejszej precyzji dają w wyniku wartość najbliższą wartości konwertowanej możliwą do zapisania w nowym typie. Jeżeli wartość jest spoza zakresu to wynik konwersji jest nieokreślony. Konwersja wartości typu zmiennoprzecinkowego do typu całkowitego Konwersja powoduje zawsze obcięcie części ułamkowej. W niektórych okolicznościach, w zależności od implementacji część ułamkowa liczby ujemnej jest odcinana w górę lub w dół. Jeżeli wartość konwertowana jest spoza zakresu typu docelowego to wynik jest nieokreślony. Podczas konwersji z typów całkowitych do zmiennoprzecinkowych może nastąpić zmniejszenie dokładności.
Konwersje typów (4) Automatyczne konwersje przy obliczaniu wyrażeń Kolejność konwersji: Jeżeli jeden z operandów jest typu long double, to drugi jest konwertowany do typu long double, W przeciwnym wypadku, jeżeli jeden z operandów jest double, to drugi jest konwertowany do double, W przeciwnym wypadku, jeżeli jeden z operandów jest float, to drugi jest konwertowany do float, W przeciwnym wypadku następuje promocja do typu int, Jeżeli jeden operand jest typu unsigned long, to drugi jest konwertowany do typu unsigned long, W przeciwnym wypadku, jeżeli jeden operand jest typu long int a drugi unsigned, int, to jeżeli long int może reprezentować wszystkie wartości typu unsigned int to unsigned int jest konwertowany do long int, w przeciwnym wypadku oba są konwertowane do unsigned long int, W przeciwnym wypadku, jeżeli jeden operand jest typu long, to drugi jest konwertowany do long, W przeciwnym wypadku, jeżeli jeden operand jet typu unsigned, to drugi jest konwertowany do unsigned, W przeciwnym wypadku oba operandy są typu int.
Konwersje typów (5) Konwersje wskaźników W momencie przypisywania, inicjalizowania, porównywania lub używania wskaźników mogą zachodzić konwersje: Stałe wyrażenie, którego wartość wynosi zero jest konwertowane do wskaźnika nazywanego wskaźnikiem pustym (null pointer). Wartość takiego wskaźnika jest różna od jakiegokolwiek wskaźnika wskazującego na dowolny obiekt. Reprezentacja bitowa wartości wskaźnika pustego nie jest równa reprezentacji bitowej liczby całkowitej int o wartości 0, Typ void * może być niejawnie tłumaczony do typu T *, gdzie T jest dowolnym typem. Każdy wskaźnik wskazujący na typ, który nie jest modyfikowany przy użyciu const lub volatile może zostać skonwertowany do typu void *, Wskaźnik do funkcji może zostać skonwertowany do typu void * o ile void * ma wystarczającą ilość bitów do przechowania tego wskaźnika, Wyrażenie typu "array of T" może być skonwertowane do wskaźnika na pierwszy element tej tablicy, Wyrażenie typu "funkcja zwracająca T" jest konwertowane do "wskaźnik do funkcji zwracającej T"
Konwersje typów (6) Przykład: #include int main(int argc, char *argv[]) { int pln_amount, euro_rate; float euro_amount; pln_amount = 100; euro_rate = 3; euro_amount = pln_amount/euro_rate; printf("Kwota euro po automatycznej konwersji: %f\n", euro_amount); euro_amount = ((float) pln_amount) / ((float) euro_rate); printf("Kwota euro po konwersji użytkownika: %f\n", euro_amount); euro_amount = (float)pln_amount/euro_rate; printf("Kwota euro po konwersji użytkownika i automatycznej konwersji: %f\n", euro_amount); system("PAUSE"); return 0; }
Dyrektywy preprocesora (1) #define identyfikator ciag_znakow Dalsze wystąpienia identyfikatora zostaną zastąpione ciągiem znaków. Przykład: #define PI 3, dlugosc_okregu = 2*PI*r; Zostaje zmienione na: dlugosc_okregu = 2*3, *r; #undef identyfikator Unieważnia poprzednią definicję
Dyrektywy preprocesora (2) #define identyfikator_1(idnetyfikator_2,..., identyfikator_N) ciag_znakow Tworzenie makrodefinicji. Przykład: #define KWADR(a) ((a)*(a))... x = KWADR(y + 3); Zostaje zamienione na: x = ((y + 3)*(y + 3));
Dyrektywy preprocesora (3) Dyrektywy kompilacji warunkowej: #ifdef nazwa #if defined nazwa #ifndef #if !defined Sprawdzenie, czy dana nazwa została zdefiniowana #endif #else #elif
Dyrektywy preprocesora (4) #error ciag_znakow Kompilacja zostaje przerwana i wypisany zostaje komunikat o błędzie, którego częścią jest ciag_znakow. #line stala nazwa_pliku Kompilator uznaje, że jest to linia programu o numerze wskazanym przez parametr stala w pliku o nazwie wskazanej przez drugi parametr. # Dyrektywa pusta – ignorowana przez kompilator.
Dyrektywy preprocesora (5) Nazwy predefiniowane: __LINE__ Numer linijki pliku. __NAME__ Nazwa kompilowanego pliku. __DATE__ Data w momencie kompilacji.
Dyrektywy preprocesora (6) #include #include "nazwa_pliku" Włącza wskazany plik do kompilowanego kodu źródłowego. W pierwszej postaci plik jest poszukiwany w katalogach zależnych od kompilatora. Druga postać dyrektywy powoduje poszukiwanie pliku w aktuualnym katalogu, a jeśli nie zostanie znaleziony, to jest poszukiwany w taki sam sposób jak w pierwszym przypadku.
Standardy kodowania (1) - identyfikacja pliku źródłowego /* NAZWA PLIKU : library.c NAZWA MODUŁU: moduł obsługi biblioteki WERSJA : 1.01 KROTKI OPIS : zbiór funkcji odpowiedzialnych za zapis wypożyczeń i zwrotów książek oraz za dodawanie i usuwanie książek z księgozbioru AUTOR : Jan Kowalski DATA : MODYFIKACJA: wprowadzono dodatkowy parametr do funkcji fi_liczba_wypozyczen DATA: AUTOR MODYFIKACJI: Józef Malinowski */
Standardy kodowania (2) Komentarze: - komentarze nie powinny wyjaśniać elementów struktury języka, -przy komentarzach dotyczących zmian można podać datę i nazwisko osoby modyfikującej Nazewnictwo - nazwy powinny wyrażać znaczenie obiektu w systemie, - nazwy zdefiniowane w programie powinny wywodzić się z ustalonego języka, - należy stosować spójny system prefiksów identyfikujących moduł
Standardy kodowania (3) Strukturalność i modularność kodu: - zaleca się pisanie funkcji widocznych w całości na ekranie, - logicznie spójne fragmenty kodu powinny być implementowane w postaci funkcji, - nie należy umieszczać w jednej linii kodu więcej niż jednej instrukcji, - funkcje związane z jednym obszarem tematycznym powinny być w miarę możliwości umieszczane w jednym pliku, - należy unikać instrukcji goto, - należy unikać wielokrotnego użycia instrukcji return w funkcji, - w instrukcjach switch należy unikać sytuacji, w których sterowanie przechodzi do instrukcji oznaczonych kolejną etykietą, - należy unikać zbyt skomplikowanych warunków logicznych, - należy unikać wykorzystania operatora przecinka
Organizacja plików programu (1) Funkcja z pliku A może mieć dostęp do jakichkolwiek zmiennych/funkcji globalnych z pliku B, gdy w pliku A umieszczono deklarację (nie definicje) tych zmiennych/funkcji. Plik B: int a; double b; char c; Plik A: extern int a; extern double b; extern char c;
Organizacja plików programu (2) Wygodnie jest umieszczać wszystkie deklaracje w osobnym pliku, który na etapie linkowania włączany jest do pliku. Plik taki określa się mianem pliku nagłówkowego. Zwyczajowo plik taki ma rozszerzenie.h. Przykład: naglowek.h extern int a; extern int b; void przywitanie_1(); void przywitanie_2(); void przywitanie_3(); void przywitanie_4();
Organizacja plików programu (3) main.c #include #include "naglowek.h" int main() { przywitanie_1(); przywitanie_2(); przywitanie_3(); przywitanie_4(); printf("%d\n", a); system("PAUSE"); return 0; } void przywitanie_1(){ printf("Bonjour\n"); a+=1;} void przywitanie_2(){ printf("Good morning\n"); a+=1;}
Organizacja plików programu (4) source2.c #include #include "naglowek.h" int a = 0; int b = 0; void przywitanie_3(){ printf("Dzien dobry\n"); a+=1; przywitanie_1(); } void przywitanie_4(){ printf("Guten Tag!\n"); a+=1; przywitanie_2(); }