ansi C
Celem zajęć jest zapoznanie studentów z językiem programowania C.
literatura
Literatura papierowa:
- B.W.Kernighan, D.M.Ritchie: Język ANSI C. WNT, Warszawa 1994.
Literatura elektroniczna:
- Standard ANSI C
- Opis bibliotek standardowych
- Steve Summit: Introductory Class Notes oraz Intermediate Class Notes
- Marshall Brain: How C Programming Works
- Steven Holmes: C Programming
- Robert Chwastek: Język C
terminy
- wykład: poniedziałek 14-16 s.25 (M.Piotrów)
- laboratorium: środa 18-20 s.110
Laboratorium
zasady zaliczenia przedmiotu
- ogólnie:
- W semestrze będzie opublikowanych (na tej stronie) 12 list z zadaniami i projekt. Zadania będą publikowane w internecie na kwadrans przed rozpoczęciem zajęć. Za każde poprawnie zaprogramowane zadanie będzie można otrzymać do 5 punktów. Na trzech ostatnich zajęciach trzeba będzie zrealizować indywidualnie przydzielony projekt, za który można będzie dostać do 20 punktów. Dodatkowe 20 punktów będzie można uzyskać z kolokwium przeprowadzonego w trakcie wykładu pod koniec semestru. W czasie semestru można więc zebrać do 100 punktów.
- obecności:
- Zadania do zaprogramowania będą ogłaszane tuż przed zajęciami, dlatego obecność na zajęciach jest obowiązkowa.
- prezentacje:
- Zadania należy realizować i prezentować w czasie trwania pracowni. Kto nie zdąży, może zaprogramować zadanie w domu i pokazać je w następnym tygodniu na początku zajęć. Za zadanie zrobione na pracowni można dostać do 5 punktów, a za zadanie spóżnione o tydzień do 3 punktów (spóźnienia ponad tygodniowe nie będą w ogóle punktowane). W trakcie prezentacji programu trzeba się liczyć z pytamiami dotyczącymi zadania: metoda rozwiązania, zastosowane konstrukcje językowe, itp.
- oceny:
- Aby zaliczyć laboratorium na ocenę dostateczną trzeba do końca semestru zdobyć 50 punktów; na ocenę bardzo dobra trzba będzie zgromadzić 90 punktów; oceny pośrednie pozostają w liniowej zależności od przedstawionych wymagań granicznych.
listy zadań
- 4 października 2006 (lista 1):
-
-
Napisz program, który wczytuje liczbę rzeczywistą oznaczającą prędkość
pojazdu liczoną w km/h. Oblicz i wypisz, ile wynosi ta prędkość
liczona w mi/h i m/s.
Uwaga: 1[km]=0.621371[mi]. - Napisz program, który wczytuje liczbę całkowitą i sprawdza, czy jest to liczba pierwsza.
-
Napisz program, który wczytuje liczbę rzeczywistą oznaczającą prędkość
pojazdu liczoną w km/h. Oblicz i wypisz, ile wynosi ta prędkość
liczona w mi/h i m/s.
- 11 października 2006 (lista 2):
-
-
Napisz program, który wczytuje długość ciągu, a później ciąg liczb
całkowitych. Określ jaka jest w tym ciągu wartość najmniejsza
i największa.
Uwaga: Program nie powinien spamiętywać wczytywanych danych. - Napisz program, który wczytuje dwie liczby naturalne i wypisuje ich największy wspólny dzielnik i najmniejszą wspólną wielokrotność.
-
Napisz program, który wczytuje długość ciągu, a później ciąg liczb
całkowitych. Określ jaka jest w tym ciągu wartość najmniejsza
i największa.
- 18 października 2006 (lista 3):
-
-
Napisz program, który przepisze tekst ze standardowego wejścia na
standardowe wyjście, i który w trakcie działania przytnie długie linie
do 63 znaków.
Uwaga: Skorzystaj z funkcji fgets(). -
Napisz program, który wczytuje ze standardowego wejścia kolejne znaki,
aż do napotkania symbolu EOF. Na końcu program powinien
wypisać na standardowym wyjściu dla błędów statystykę dotycząca
wczytanego tekstu: ile było wszystkich przeczytanych znaków, ile było
białych znaków, z ilu linii składał się tekst oraz jaka była długość
najdłuższego wiersza.
Uwaga: Program powinien czytać znak po znaku funkcją getchar(). -
Napisz program, który sprawdzi czy podane przez użytkownika argumenty
wywołania programu są nieujemnymi całkowitymi liczbami dziesiętnymi.
Dla każdego argumentu który jest liczbą, program powinien wypisać ile
bitów jest potrzebnych do jej zapamiętania w systemie dwójkowym.
Uwaga: Podane liczby mogą być bardzo długie, nie mieszczące się w zakresie żadnego standardowego typu całkowitoliczbowego.
-
Napisz program, który przepisze tekst ze standardowego wejścia na
standardowe wyjście, i który w trakcie działania przytnie długie linie
do 63 znaków.
- 25 października 2006 (lista 4):
-
-
Zaprogramuj prosty kalkulator. Ma on działać w pętli: najpierw wczytuje
liczbę rzeczywistą, potem symbol działania arytmetycznego, i znowu
liczbę a potem znowu symbol działania, itd. Program ma działać w pętli,
wyliczając na bieżąco wyniki operacji arytmetycznych, aż do wczytania
symbolu równości (=).
Uwaga: Dopuszczalne operacje arytmetyczne to: dodawanie (+), odejmowanie (-), mnożenie (*) i dzielenie (/).
Uwaga: Program należy zabezpieczyć przed wczytaniem błędnych danych. -
Napisz program, który wywołany z dwoma parametrami r (liczba
rzeczywista r≥0) i k (liczba naturalna
k<100) wyliczy i stablicuje wartości pi
dla i=0...k-1 określone następującym wzorem rekurencyjnym:
p0 = 1
pi = (r/pi-1+pi-1)/2 : i>0
Na koniec program ma wypisać w dwóch kolumnach następujące wyniki: w pierwszej kolumnie wartości pi a w drugiej różnicę pomiędzy pi a pierwiastkiem kwadratowym z r (użyj funkcji sqrt z biblioteki matematycznej).
Uwaga: Program należy zabezpieczyć przed podaniem błędnych danych.
-
Zaprogramuj prosty kalkulator. Ma on działać w pętli: najpierw wczytuje
liczbę rzeczywistą, potem symbol działania arytmetycznego, i znowu
liczbę a potem znowu symbol działania, itd. Program ma działać w pętli,
wyliczając na bieżąco wyniki operacji arytmetycznych, aż do wczytania
symbolu równości (=).
- 8 listopada 2006 (lista 5):
-
- Napisz program, który wczytuje liczbę całkowitą reprezentującą rok. Program ma sprawdzić, czy podany rok jest przestępny i wypisać odpowiedni komunikat. Do testowania przestępności roku użyj funkcji czyPrzestepny(int), która zwraca 1 gdy rok jest przestępny, albo 0 w przeciwnym przypadku.
- (kontynuacja poprzedniego zadania)
Napisz program, który wczytuje dwie liczby całkowite reprezentujące odpowiednio miesiąc i rok. Program ma sprawdzić, ile dni ma zadany miesiąc (luty ma różną liczbę dni w zależności od roku). Skorzystaj z dwuwymiarowej tablicy dniWMiesiacu, w której są zapisane liczby dni w poszczególnych miesiącach, osobno dla lat zwykłych i przestępnych. Do tablicy tej powinieneś się odwoływać w nastepujacy sposób: dniWMiesiacu[czyPrzestepny(rok)][mies]. - (kontynuacja poprzedniego zadania)
Napisz program, który wczytuje trzy liczby całkowite reprezentujące odpowiednio dzień, miesiąc i rok. Program ma wyliczyć, ile dni upłynęło do zadanego dnia od początku roku, oraz ile dni zostało jeszcze do jego końca włącznie z tym dniem. Skorzystaj z pomocniczej tablicy dwuwymiarowej dniOdPoczRoku, która będzie zawierała informacje o liczbie dni od poczatku roku do końca poprzedniego miesiąca minus jeden dzień. Obie wartości będziesz mógł wtedy bardzo prosto wyliczyć. Przykładowo, dystans czasowy od początku roku do 1 stycznia wynosi 0 dni, a od 1 grudnia do końca roku 31 dni. - (kontynuacja poprzedniego zadania)
Napisz program, który wczytuje trzy liczby całkowite reprezentujące odpowiednio dzień, miesiąc i rok. Program ma sprawdzić czy podana data jest prawidłowa (miesiąc z zakresu 1...12, dzień 1...). Weź też pod uwagę fakt, że kalendarz Gregoriański obowiązuje od 15 października 1582. W swoim programie koniecznie zaimplementuj funkcję porownanieDat(int,int,int,int,int,int) porównującą dwie daty, która będzie odpowiadać wartością -1 gdy pierwsza data jest późniejsza niż druga, 0 gdy daty są takie same, 1 gdy pierwsza data jest wcześniejsza niż druga. - (kontynuacja poprzedniego zadania)
Napisz program, który dwukrotnie wczytuje trzy liczby całkowite reprezentujące odpowiednio dzień, miesiąc i rok (wczytujemy dwie daty). Program ma sprawdzić, czy podane daty są poprawne, a potem wyliczyć, ile dni upłynęło od pierwszej daty do drugiej. Liczba tych dni ma wynieść 0 gdy obie daty są takie same, a w przypadku gdy pierwsza data jest późniejsza niż druga program powinien wypisać liczbę ujemną.
- 15 listopada 2006 (lista 6):
-
-
Dość często potrzebujemy ustawiać lub sprawdzać wartości pojedynczych
bitów w słowie typu int lub unsigned int.
Zdefiniuj dwie funkcje: jedną do odczytywania wartości określonego bitu
(funkcja ma zwracać wartość 0 lub 1), a drugą do ustawiania wartości
określonego bitu (funkcja ma ustawić bit na 1 tylko wtedy gdy parametr
ma wartość różną od 0):
int jakiBit (unsigned &komorka, int nrBitu);
void ustawBit (unsigned &komorka, int nrBitu, int wartosc);
Przy programowaniu tych funkcji skorzystaj z operatorów bitowych. Następnie napisz krótki program testowy, sprawdzający ich poprawne działanie (numer bitu to wartość z zakresu od 0 do (sizeof(unsigned)<<3)-1). - (kontynuacja poprzedniego zadania)
Zaprogramuj analogiczne funkcje działające na całych tablicach:
int jakiBit (unsigned *tablica, int nrBitu);
void ustawBit (unsigned *tablica, int nrBitu, int wartosc);
Przy programowaniu tych funkcji skorzystaj z poprzednio zdefiniowanych procedur. Następnie napisz krótki program testowy, sprawdzający ich poprawne działanie (numer bitu to nieujemna liczba całkowita). - (kontynuacja poprzedniego zadania)
Zadeklaruj globalną tablicę unsigned sito[] o najmniejszym możliwym rozmiarze, która będzie zawierała co najmniej MAX_BIT bitów (wartość MAX_BIT zdefiniuj dyrektywą #define dla preprocesora, na przykład jako 221). Zaprogramuj dodatkową funkcję, która wypełni tą tablicę wartościami 1 tylko na tych pozycjach, które odpowiadają liczbom pierwszym (na i-tej pozycji znajduje się 1 tylko wtedy, gdy liczba i jest pierwsza):
void sitoEratostenesa ();
Popraw funkcje jakiBit() i ustawBit(), tak aby sprawdzały one wartość parametru nrBitu (dla wartości <0 lub ≥MAX_BIT funkcja jakiBit() zawsze zwraca 0 a funkcja ustawBit() nie robi nic).
Napisz też funkcję, która dla zadanej liczby całkowitej sprawdzi, czy jest ona pierwsza (zwracana wartość to 1) czy nie (zwracana wartość to 0); twoja funkcja powinna poprawnie odpowiadać dla wszystkich wartości typu int, również dla liczb <0 i ≥MAX_BIT.
int czyPierwsza (int liczba);
Następnie napisz krótki program testowy, wypisujący wszystkie liczby pierwsze niewiększe od zadanej liczby wczytanej ze standardowego wejścia. Komunikaty zachęcające do wpisywania danych należy kierować na standardowe wyjście dla błędów. - (kontynuacja poprzedniego zadania)
Podziel poprzednie zadanie na pięć części: plik nagłówkowy z deklaracjami funkcji do odczytywania i ustawiania bitów, plik nagłówkowy z deklaracją funkcji do inicjalizacji sita Eratostenesa i z deklaracją funkcji do testowania pierwszości liczby (pliki nagłówkowe powinny posiadać dyrektywy włączania warunkowego #ifndef i #endif dla preprocesora), pliki źródłowe z implementacjami funkcji z odpowiednich plików nagłówkowych (zadeklaruj w jednym statyczną tablicę bitową z sitem Eratostenesa) a także plik źródłowy z funkcją main(), która w pętli będzie wczytywała liczbę całkowitą i wypisywała komunikat o jej pierwszości.
-
Dość często potrzebujemy ustawiać lub sprawdzać wartości pojedynczych
bitów w słowie typu int lub unsigned int.
Zdefiniuj dwie funkcje: jedną do odczytywania wartości określonego bitu
(funkcja ma zwracać wartość 0 lub 1), a drugą do ustawiania wartości
określonego bitu (funkcja ma ustawić bit na 1 tylko wtedy gdy parametr
ma wartość różną od 0):
- 29 listopada 2006 (lista 7):
-
-
Zaprojektuj strukturę węzła w drzewie binarnych poszukiwań; w węźle
będziemy pamiętać napisy (tablice znaków) o różnej długości.
Zdefiniuj parę funkcji, która będzie odpowiedzialna za utworzenie
nowego węzła w pamięci wolnej i rekurencyjne usunięcie go wraz z całym
poddrzewem (napis pamiętany w węźle także ma być przechowywany
w pamięci wolnej).
Wezel * utworz (char *nap);
void usun (Wezel *wezel);
Następnie dopisz rekurencyjne funkcje do wstawiania nowej wartości (napisu) do węzła i sprawdzania czy zadana wartość (napis) jest w tym drzewie pamiętana.
Wezel * wstaw (char *nap);
Wezel * szukaj (char *nap);
Podziel to zadanie na plik nagłówkowy i plik źródłowy. - (kontynuacja poprzedniego zadania)
Napisz program, który wczytuje ze standardowego wejścia wiersz po wierszu, aż do napotkania symbolu EOF. Wczytane wiersze program ma umieszczać w drzewie binarnych poszukiwań zdefiniowanym w porzednim zadaniu. Następnie program powinien wypisać wszystkie przeczytane wiersze w porządku inorder na standardowe wyjście a na końcu usunąć całe drzewo.
void inorder (Wezel *wezel, FILE *wy);
Procedura wypisująca zawartość drzewa powinna być rekurencyjna. - (kontynuacja poprzedniego zadania)
Poprzednie zadanie należy uzupełnić procedurami, które policzą wysokość drzewa i liczbę liści w drzewie. Po wczytaniu danych należy te informacje wypisać na standardowym wyjściu dla błędów (przed usunięciem drzewa).
int wysokosc (Wezel *wezel);
int liscie (Wezel *wezel);
Procedury te także mają być rekurencyjne.
-
Zaprojektuj strukturę węzła w drzewie binarnych poszukiwań; w węźle
będziemy pamiętać napisy (tablice znaków) o różnej długości.
Zdefiniuj parę funkcji, która będzie odpowiedzialna za utworzenie
nowego węzła w pamięci wolnej i rekurencyjne usunięcie go wraz z całym
poddrzewem (napis pamiętany w węźle także ma być przechowywany
w pamięci wolnej).
- 6 grudnia 2006 (lista 8):
-
- Napisz program, który wczyta nazwę pliku tekstowego, a następnie wypisze jego zawartość, o ile plik istnieje, na standardowym wyjściu. Plik należy napierw otworzyć do czytania, potem przeczytać go linia po linii, a na końcu zamknąć. Jeśli linia jest dłuższa niż 79 znaków, to pozostałe znaki w tej linii należy zignorować.
- W tekstowym pliku z danymi jest zapisanych N liczb rzeczywistych a1, ..., aN. Format pliku z danymi jest taki, że w pierwszej linii jest zapisana liczba całkowita N typu int, a w kolejnych liniach pooddzielane białymi znakami wartości rzeczywiste a1, ..., aN typu double. Należy te liczby odczytać, zapamiętując je równocześnie w dynamicznie przydzielonej tablicy. Na koniec trzeba je zapisać do pliku binarnego (bez wartości N na początku) i zwolnić przydzieloną pamięć.
- (kontynuacja poprzedniego zadania)
W binarnym pliku z danymi jest zapisane są liczby typu double. Określ ile tych liczb jest zapisanych w pliku, przydziel dynamicznie odpowiedni obszar pamięci i wczytaj je tam. Następnie policz średnią arytmetyczną i odchylenie standardowe i wypisz te wielkości na standardowym wyjściu. Na końcu zwolnij przydzieloną pamięć.
- 13 grudnia 2006 (lista 9):
-
-
Napisz program, który przekopiuje zawartość jednego pliku do drugiego.
Nazwy plików mają być podawane jako argumenty wywołania programu.
Jeżeli plik docelowy już istnieje, program powinien się zapytać
użytkownika, czy chce zastąpić plik, dołączyć pierwszy do drugiego,
zmienić nazwę drugiego, czy też zrezygnować.
Uwaga: Sprawdź, czy program wywołano z prawidłową liczbą argumentów, oraz czy podane nazwy są poprawnymi nazwami plików i czy pierwszy z nich istnieje w lokalnym systemie. -
Napisz program, który porówna ze sobą zawartości dwóch plików
z dokładnością do białych znaków w obrębie linii.
Nazwy plików mają być podawane jako argumenty wywołania programu.
Po napotkaniu pierwszej różnicy program powinien wypisać numer wiersza
i numer pozycji w wierszu, w którym występuje różnica, po czym przerwać
dalsze porównywanie.
Uwaga: Sprawdź, czy program wywołano z prawidłową liczbą argumentów, oraz czy podane nazwy są nazwami istniejących plików w lokalnym systemie.
-
Napisz program, który przekopiuje zawartość jednego pliku do drugiego.
Nazwy plików mają być podawane jako argumenty wywołania programu.
Jeżeli plik docelowy już istnieje, program powinien się zapytać
użytkownika, czy chce zastąpić plik, dołączyć pierwszy do drugiego,
zmienić nazwę drugiego, czy też zrezygnować.
- 20 grudnia 2006 (lista 10):
-
-
Napisz program, który (de)szyfruje podany w linii poleceń plik.
Kluczem powinno być jedno słowo podawane jako drugi argument wywołania
programu.
Szyfrowanie powinno cyklicznie "przykładać" klucz do pliku
i odpowiednio modyfikować jego zawartość.
Na parach kolejnych znaków pliku i klucza wykonuj operację XOR
(^); kiedy zabraknie liter klucza, należy zacząć ponownie od
jego początku.
Metodę szyfrowania tekstu zadanym kluczem zaprogramuj koniecznie jako
oddzielną funkcję (zadeklarowaną w pliku nagłówkowym).
Wskazówka: Sprawdź, czy program wywołano z prawidłową liczbą argumentów, oraz czy podana nazwa pliku jest nazwą istniejącego pliku w lokalnym systemie. -
Napisz program do mnożenia macierzy.
Program najpierw wczytuje liczbę wierszy i liczbę kolumn macierzy
A, alokuje pamięć na tę macierz i wczytuje kolejno jej
elementy.
Potem ten sam proces czytania powtarza dla macierzy B.
Zauważ, że liczba kolumn w macierzy A musi być taka sama jak
liczba wierszy w macierzy B.
Następnie program powinien obliczyć iloczyn tych macierzy i zapamiętać
go w dynamicznie przydzielonej pamięci.
Na końcu wypisz wynik na standardowe wyjście.
Wskazówka: Macierz powinna być pamiętana w tablicy tablic (pierwsza tablica zawiera wskaźniki tablic jednowymiarowych).
Uwaga: Przygotuj poprawne dane w pliku tekstowym i uruchom program podając mu nazwę tego pliku.
-
Napisz program, który (de)szyfruje podany w linii poleceń plik.
Kluczem powinno być jedno słowo podawane jako drugi argument wywołania
programu.
Szyfrowanie powinno cyklicznie "przykładać" klucz do pliku
i odpowiednio modyfikować jego zawartość.
Na parach kolejnych znaków pliku i klucza wykonuj operację XOR
(^); kiedy zabraknie liter klucza, należy zacząć ponownie od
jego początku.
Metodę szyfrowania tekstu zadanym kluczem zaprogramuj koniecznie jako
oddzielną funkcję (zadeklarowaną w pliku nagłówkowym).
projekt
Przydział zadań projektowych: (html)
ranking
Punktacja z laboratoriów: (html)
Ogłoszenia
- 21.12.2006
- Zostały przydzielone indywidualne projekty. Termin ich realizacji mija 17 stycznia 2006 r.
- 10.10.2006
- Pierwszy ranking ukaże się na początku listopada 2006 r.