Język C - ćwiczenie 1

Podobne dokumenty
1 Podstawy c++ w pigułce.

Podstawy programowania skrót z wykładów:

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Temat 1: Podstawowe pojęcia: program, kompilacja, kod

1 Podstawy c++ w pigułce.

Wstęp do Programowania, laboratorium 02

Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227 WYKŁAD 7 WSTĘP DO INFORMATYKI

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

2 Przygotował: mgr inż. Maciej Lasota

Część 4 życie programu

7. Pętle for. Przykłady

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Instrukcja do ćwiczeń nr 4 typy i rodzaje zmiennych w języku C dla AVR, oraz ich deklarowanie, oraz podstawowe operatory

Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Ćwiczenie 1. Podstawy. Wprowadzenie do programowania w języku C. Katedra Metrologii AGH

Programowanie strukturalne i obiektowe

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Programowanie w języku C++ Grażyna Koba

Laboratorium 1 Temat: Przygotowanie środowiska programistycznego. Poznanie edytora. Kompilacja i uruchomienie prostych programów przykładowych.

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

Zmienne, stałe i operatory

Operatory. Operatory bitowe i uzupełnienie informacji o pozostałych operatorach. Programowanie Proceduralne 1

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

3. Instrukcje warunkowe

Programowanie w języku Python. Grażyna Koba

Wstęp do programowania. Wykład 1

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Mikrokontroler ATmega32. Język symboliczny

Podstawy programowania w języku C

Języki i metodyka programowania. Wprowadzenie do języka C

Język C - podstawowe informacje

( wykł. dr Marek Piasecki )

Cwiczenie nr 1 Pierwszy program w języku C na mikrokontroler AVR

Podstawy programowania C. dr. Krystyna Łapin

Instrukcja wyboru, pętle. 2 wykład. Podstawy programowania - Paskal

Tablice (jedno i wielowymiarowe), łańcuchy znaków

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

PODSTAWY INFORMATYKI 1 PRACOWNIA NR 6

Podstawy programowania. Wykład: 4. Instrukcje sterujące, operatory. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Podstawy programowania w języku C i C++

JAVA. Platforma JSE: Środowiska programistyczne dla języka Java. Wstęp do programowania w języku obiektowym. Opracował: Andrzej Nowak

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Podstawy Programowania. Wykład 1

Lab 9 Podstawy Programowania

Wstęp do programowania INP003203L rok akademicki 2016/17 semestr zimowy. Laboratorium 1. Karol Tarnowski A-1 p.

Komentarze w PHP (niewykonywane fragmenty tekstowe, będące informacją dla programisty)

I - Microsoft Visual Studio C++

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 2. Karol Tarnowski A-1 p.

Podstawy Programowania Podstawowa składnia języka C++

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Język C zajęcia nr 11. Funkcje

Wstęp do programowania INP001213Wcl rok akademicki 2018/19 semestr zimowy. Wykład 2. Karol Tarnowski A-1 p.

Programowanie komputerowe. Zajęcia 1

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Proste algorytmy w języku C

Pętle. Dodał Administrator niedziela, 14 marzec :27

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

Podstawy Programowania C++

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

Programowanie Komputerów

Podstawy programowania - 1

Wstęp do programowania

Instrukcje warunkowe i skoku. Spotkanie 2. Wyrażenia i operatory logiczne. Instrukcje warunkowe: if else, switch.

Wykład VII. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

PLAN WYNIKOWY PROGRAMOWANIE APLIKACJI INTERNETOWYCH. KL IV TI 6 godziny tygodniowo (6x15 tygodni =90 godzin ),

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Struktura pliku projektu Console Application

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Laboratorium 1: Podstawy języka c. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

Wskaźniki w C. Anna Gogolińska

dr inż. Jarosław Forenc

4. Funkcje. Przykłady

Programowanie I C / C++ laboratorium 02 Składnia pętli, typy zmiennych, operatory

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

Wstęp do Informatyki i Programowania Laboratorium: Lista 0 Środowisko programowania

Język C, tablice i funkcje (laboratorium, EE1-DI)

Języki i paradygmaty programowania

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

Język C : programowanie dla początkujących : przewodnik dla adeptów programowania / Greg Perry, Dean Miller. Gliwice, cop

Operatory AND, OR, NOT, XOR Opracował: Andrzej Nowak Bibliografia:

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

Podstawy Informatyki Wprowadzenie do języka C dr inż. Jarosław Bułat

Informacja o języku. Osadzanie skryptów. Instrukcje, komentarze, zmienne, typy, stałe. Operatory. Struktury kontrolne. Tablice.

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

Wyrażenia arytmetyczne

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

LABORATORIUM 3 ALGORYTMY OBLICZENIOWE W ELEKTRONICE I TELEKOMUNIKACJI. Wprowadzenie do środowiska Matlab

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

Metodyki i Techniki Programowania MECHANIZM POWSTAWANIA PROGRAMU W JĘZYKU C PODSTAWOWE POJĘCIA

Transkrypt:

Język C - ćwiczenie 1 Podstawy 1 Kompilacja Program napisany w dowolnym języku musi być przed wykonaniem przełożony na kod maszynowy komputera na którym jest wykonywany. Może to odbywać się na dwa sposoby: każdorazowo podczas wykonania - program jest interpretowany; tylko jeden raz po napisaniu - program jest kompilowany. Skrypty powłoki, z którymi miałeś(aś) do czynienia w poprzedniej serii ćwiczeń laboratoryjnych, są programami interpretowanymi. Takie programy są łatwe do przenoszenia między komputerami różnych architektur. Dodatkowo można je łatwo poprawiać między kolejnymi uruchomieniami. Niestety, interpretacja programu zajmuje czas przez co wykonuje się on znacznie wolniej - praktycznie nie spotyka się np. gier napisanych w językach interpretowanych. Jeżeli piszemy złożony program który nie wymaga częstych modyfikacji, lepiej skorzystać z języka kompilowanego. Skompilowany program wykonuje się szybko, ale jest przystosowany tylko do jednej architektury komputera i jednego systemu operacyjnego. Większość dużych programów (pakiety biurowe, przeglądarki internetowe, gry, narzędzia obliczeniowe) są pisane w językach kompilowanych i dostarczane w formie binarnej, czyli gotowej do uruchomienia. Język C, z którym będziesz zapoznawać się do końca tego semestru (a także w następnym semestrze) jest jednym z najpopularniejszych języków kompilowanych. 1.1 Od kodu do pakietu binarnego Kompilacja nie jest jedynym procesem wykonywanym podczas przygotowywania programu napisanego w języku C. Program taki składa się z jednego lub więcej plików tekstowych z rozszerzeniem.c. Są to pliki źródłowe. Program może też zawierać tzw. pliki nagłówkowe z rozszerzeniem.h. Proces przygotowania programu wykonywalnego, nazywany budowaniem programu przedstawia rysunek 1 Pisanie kodu Po zaprojektowaniu funkcjonalności programu (czyli wymyśleniu co i jak ma robić) oraz wybraniu/opracowaniu algorytmów i struktur danych przychodzi pora na pisanie kodu programu. Kod programu jest zapisywany w zwykłych plikach tekstowych i do jego przygotowania wystarczy dowolny edytor tekstu. Częściej stosuje się jednak edytory z Kolorowaniem składni, czyli zaznaczające kolorami poszczególne elementy języka. Podczas pisania kodu należy trzymać się jednego standardu kodowania, określającego sposób umieszczania poszczególnych elementów programu względem siebie. Jest to szczególnie istotne gdy pracuje się w zespole programistów; w takiej sytuacji jednolity styl znacznie ułatwia pracę wszystkich. Dokumentacja kodu Pisany program musi być udokumentowany w sposób umożliwiający jednoznaczne zrozumienie przeznaczenia i działania jego poszczególnych fragmentów. Najprostszym (i bardzo niedocenianym) sposobem jest uzupełnienie kodu o komentarze, czyli fragmenty nie wykorzystywane w procesie budowania i przeznaczone do czytania przez ludzi przeglądających kod. Brak komentarzy często uniemożliwia skorzystanie z kodu nawet jego własnemu autorowi, który wraca do niego po pewnym czasie. Bardziej zaawansowane techniki dokumentacji często polegają na przygotowywaniu dokładnych opisów działania elementów programu. Często stosuje się narzędzia które analizują komentarze opatrzone specjalnymi znacznikami, przygotowując z nich kompletną dokumentację w wybranym formacie (np. pdf lub html). Dokumentacja jest później wykorzystywana na etapie testów. W programach pisanych przez profesjonalne zespoły programistów dokumentacja jest często obszerniejsza niż sam kod programu! 1

Plik źródłowy Plik nagłówkowy Preprocessing Kompilacja Pliki binarne i biblioteki Plik źródłowy Plik nagłówkowy Preprocessing Kompilacja Łączenie Plik wyjściowy Plik źródłowy Preprocessing Kompilacja Rysunek 1: Ilustracja procesu budowy programu Preprocessing Pierwszą czynnością podczas przygotowania kodu jest preprocessing, czyli wstępna analiza kodu. Jest on wykonywany osobno dla każdego pliku.c przez program nazywany preprocesorem. Działaniem preprocesora steruje programista dodając do programu dyrektywy preprocesora, w języku C oznaczone znakiem #. Najczęstsze zadania preprocesora to dołączenie do plików źródłowych zawartości plików nagłówkowych. Często używa się go też do modyfikacji kodu, np. wyłączania niepotrzebnych fragmentów lub zamieniania fragmentów kodu na zdefiniowane słowa. Preprocesor jest uruchamiany oddzielnie dla każdego pliku źródłowego. Jeżeli zażądano dołączenia pliku nagłówkowego, jest on również analizowany. Kompilacja Po poprawnym zakończeniu działania preprocesora uruchamiany jest kompilator. Poza tłumaczeniem na kod asemblera jego zadaniem jest optymalizacja działania programu. Możliwe jest także dołączenie znaczników, umożliwiających debugowanie programu. Po wykonaniu kompilacji automatycznie uruchamiany jest asembler, który generuje kod maszynowy w formie sekwencji liczb. Podobnie jak preprocesor, kompilator operuje oddzielnie na każdym z plików źródłowych. Efektem jego działania jest zatem zestaw plików binarnych. Łączenie Zanim program będzie mógł być uruchamiany, pliki binarne wytworzone przez kompilator muszą zostać połączone w jedną całość. Jest to zadanie programu łączącego, czyli linkera. Poza połączeniem plików, linker uzupełnia plik wynikowy o dane, informujące system operacyjny o jego przeznaczeniu, czyli czy np. plik jest programem wykonywalnym, biblioteką lub sterownikiem sprzętu. Ponieważ łączeniu podlegają pliki binarne, na tym etapie mogą być dołączane pliki napisane w innym języku programowania oraz biblioteki systemowe. Plik wygenerowany przez linker może być wykonywany, ale jeżeli program ma być rozpowszechniany konieczne są jeszcze dodatkowe kroki. Łączenie dynamiczne Wiele bibliotek systemowych jest stosowana przez niemal wszystkie zainstalowane programy. W takim przypadku zamiast łączyć każdy program z biblioteką lepiej jest zainstalować bibliotekę w formie binarnej w systemie i zastosować łączenie dynamiczne. Polega ono na tym, że w programie znajdują się informacje dla systemu operacyjnego o potrzebnej bibliotece. Gdy zachodzi konieczność użycia biblioteki, system operacyjny ładuje ją do pamięci i umożliwia programowi skorzystanie z niej. Szybkość działania programów łączonych dynamicznie jest porównywalna z programami posiadającymi wbudowane biblioteki (czyli łączonymi statycznie), a ich rozmiar jest znacznie mniejszy. Gdy jednak biblioteki nie ma w systemie, program się nie wykona. 2

Debugowanie i testy Poza najprostszymi przypadkami, praktycznie każdy program zaraz po napisaniu ma błędy. Muszą one zostać usunięte zanim program zostanie dostarczony do użytkownika. W tym celu gotowy program jest testowany. Testy programu mogą być prowadzone automatycznie lub ręcznie, i mogą to być testy działania (ładuje się, nie zużywa pamięci w niekontrolowany sposób, korzysta z właściwych danych) oraz testy funkcjonalności (robi to do czego jest przewidziany). W przypadku stwierdzenia błędu musi on zostać usunięty. Niewiele błędów jest jednak oczywiste (takie zwykle wykrywane są na etapie kompilacji lub łączenia i kończą się błędem kompilatora lub linkera). Aby znaleźć błędy, program poddaje się debugowaniu. Może ono w najprostszym przypadku polegać na wypisaniu wybranych danych i stanu pracy na ekran i ich obserwacji. Bardziej zaawansowanym rozwiązaniem jest zastosowanie debuggera. Jest to program, pozwalający na załadowanie badanego programu do pamięci w sposób kontrolowany, i wykonanie go instrukcja po instrukcji. Możliwy jest też podgląd zmiennych oraz ustawianie tzw. pułapek, zatrzymujących wykonanie programu po dojściu do wybranej linii, a także wiele innych udogodnień (podgląd wątków programu, przerwań czy sygnałów systemowych). Program przeznaczony do debugowania musi być niestety odpowiednio przygotowany na etapie kompilacji. Programów takich nie da się optymalizować, przez co działają wolniej, a obecność dodatkowych znaczników zwiększa ich rozmiar. Dostarczenie do użytkownika końcowego Gotowy program, o ile nie jest wprawką programistyczną, powinien być dostarczany użytkownikowi końcowemu. Sposób dostarczenia różni się w zależności od użytkownika. Jeżeli program jest przeznaczony dla użytkowników masowych, to jest on uzupełniany o instalator (w przypadku Windows) lub jest przygotowywany pakiet instalacyjny (dla Mac OS X i Linuksa). Następnie program jest dostarczany użytkownikom wybranym kanałem dystrybucyjnym. W przypadku odbiorców indywidualnych często biorą oni aktywny udział w postawaniu oprogramowania, a dostarczenie może polegać na przekazaniu kodu źródłowego, urządzenia z wgranym programem lub niemal dowolnie innej formie dostawy. Zintegrowane środowisko programisty Jak widać, tworzenie programu jest procesem złożonym, wieloetapowym i wymagającym wielu programów. Programy te mogą być uruchamiane przez programistę ręcznie, przez skrypt powłoki lub specjalizowany program budujący (np. make). Metody te nie są jednak często stosowane, gdyż są na dłuższą metę niewygodne. Zamiast tego stosuje się programy nazywane zintegrowanym środowiskiem programisty lub IDE ang. integrated development environment. Posiada ono przynajmniej edytor tekstu z kolorowaniem składni oraz intefejs graficzny umożliwający automatyczne wykonywanie procesu budowania lub jego wybranych etapów. Kolejnym, niemal zawsze występującym elementem składowym IDE jest interfejs graficzny dla debuggera. Współcześnie jest on zawsze zintegrowany ze wspomnianym edytorem tekstu. Bardziej zaawansowane IDE mają wiele innych elementów składowych. Przykładowo, są wyposażone w narzędzia do współpracy dla grup programistów, edytory interfejsów uzytkownika czy wsparcie dla narzędzi sprzętowych do programowania mikrokontrolerów. W laboratorium podstaw informatyki będziesz korzystał(a) z prostego IDE o nazwie Code::blocks. Jego funkcjonalność jest aż nadto wystarczająca dla wykonywanych zadań, a dużą zaletą jest wieloplatformowowść - Code::blocks jest dostępne zarówno dla systemu Linux, jak i Windows. 2 Program w języku C Przyjrzyjmy się teraz przykładowemu programowi: /* program przykładowy 1 - Podstawy Informatyki Andrzej Wetula, 2012 */ #include <stdio.h> // funkcja fun1 void fun1(int M) if(m<100) return; 3

else printf("m wynosi %d\n",m); // funkcja main int main(void) int L; for(l=0;l<10;l++) printf("%d ",L); printf("\n"); fun1(100); return 0; Każdy program w C jest podzielony na funkcje. Funkcja jest fragmentem kodu, któremu programista nadał nazwę, i który może być wykonany dowolną ilość razy. W powyższym przypadku program składa się z dwóch funkcji: main oraz fun1. Program operuje na danych, które są przechowywane w zmiennych, mających określony typ. Zmienną w programie jest L; służy ona do przechowywania liczb całkowitych ze znakiem (typ int). Podstawową częścią każdego języka są słowa kluczowe. Określają one operacje jakie ma wykonać komputer. Słowami kluczowymi w powyższym programie są for, if wraz z else oraz return. Poza słowami kluczowymi w kodzie można znaleźć wywołania funkcji. Każde wywołanie polega na wpisaniu nazwy funkcji z parametrami w nawiasie okrągłym (jeżeli funkcja nie przyjmuje parametrów, wpisujemu pusty nawias). Wywołaniami funkcji są na przykład printf( %d,l); i fun1(100);. Zauważ, że funkcja printf nie występuje w kodzie, ale jest używana. Jest ona dostępna dzięki obecności w dynamicznie łączonej bibliotece systemowej, do której odwołanie wpisano do kodu w formie dyrektywy preprocesora #include <stdio.h>. Ostatnim (ale ważnym) fragmentem kodu są komentarze. Mogą one mieć postać tekstu ograniczonego znacznikami / i / albo linii rozpoczętych znacznikiem //. Formatowanie kodu i standardy kodowania Kod podzielony jest na linie, z których każda jest zakończona średnikiem. Zapomnienie wpisania średnika to najczęstszy błąd poczkątkującego (i nie tylko) programisty!. Do grupowania fragmentów kodu służą nawiasy klamrowe i. Kod objęty takimi nawiasami nazywany jest instrukcją złożoną i jest traktowany jak pojedyncza instrukcja. Wcięcia w kodzie nie są interpretowane przez kompilator i służą wyłącznie poprawie czytelności programu. Innymi słowy, powyższy kod mógłby wyglądać tak: void fun1(int M) if(m<100) return; else printf("m wynosi %d\n",m); int main(void) int L; for(l=0;l<10;l++) printf("%d ",L); printf("\n"); fun1(100); return 0; Jeżeli nie wierzysz, możesz sprawdzić że taki kod też się skompiluje. Jednak możliwość zrozumienia co on robi jest znikoma. Dlatego pamiętaj o wcięciach! Jak widzisz, C nie wymusza układu kodu. Z tego powodu robią to ludzie, ustalając standardy kodowania. Standard kodowania określa wymagania dotyczące formatowania kodu (czyli stylu wcięć i położenia nawiasów klamrowych), nazewnictwa funkcji i zmiennych czy sposobu budowania często spotykanych konstrukcji programistycznych. Podczas zajęć w laboratorium nie jest wymagane trzymanie się konkretnego standardu kodowania, ale kod powinien być czytelny i jednoznaczny, a styl powtarzalny między kolejnymi pisanymi programami. 2.1 Zmienne i typy danych Zmienne służą do przechowywania danych na których operuje program. Nazwy zmiennych wraz z typem są określane przez programistę. Określenie takie ma formę deklaracji zmiennej: int L; W powyższym przykładzie zdefiniowano zmienną o nazwie L do przechowywania liczb całkowitych ze znakiem (int). Deklaracja zmiennej powinna znajdować się na początku kodu, tuż po nazwie funckji i otwierającym nawiasie klamrowym. Nazwa zmiennej może składać się z dużych i małych liter, cyfr 4

oraz znaku, przy czym nie może zaczynać się od cyfry. Długość nazwy zmiennej może być dowolna, ale dla kompilatora znaczenie mają tylko pierwsze 32 znaki. Jeżeli stosuje się wiele zmiennych (czyli niemal zawsze), ich nazwy powinny umożliwiać łatwe określenie przeznaczenia zmiennej. Często użwyane zmienne (np. licznikowe) mogą mieć krótkie nazwy. Przed użyciem należy do zmiennej przypisać wartość. Można to zrobić podczas deklarowania: int L = 0; lub później. Jeżeli do zmiennej nie przypisano wartość, zawiera ona wartość która w momencie ładowania programu była w danej komórce pamięci, czyli w praktyce losową. Niektóre zmienne mają wartość automatycznie ustawianą na 0, ale o tym później. Typ danych określa jaki rodzaj danej może być przechowywany w zmiennej. W języku C można spotkać następujące typy danych: char - liczba całkowita 8-bitowa ze znakiem lub pojedyncza litera w kodzie ASCII (zamiennie) int - liczba całkowita ze znakiem o długości zależnej od architektury (przynajmniej 16 bitów) float - liczba zmiennoprzecinkowa 32-bitowa (zapis postaci X 2 ˆN) double - liczba zmiennoprzecinkowa 64-bitowa Dodatkowo typ można zmodyfikować przy użyciu specyfikatorów - długości: short - tylko dla typu int, wymusza długość słowa 16 bitów; long - dla typu int wymusza długość słowa 32 bity, dla double wymusza 128 bitów; long long - tylko dla typu int, wymusza długość słowa 64 bity; oraz znaku (dla typów całkowitoliczbowych): signed - wymusza znak (tak jest domyślnie); unsigned - dla typów całkowitoliczbowych, wymusza brak znaku. Przykładowo: unsigned long int Wynik; deklaruje 32-bitową zmienną całkowitoliczbową ze znakiem o nazwie Wynik. Ponieważ modyfikatory long i short są używane głównie dla typu int, można przy nich pominąć typ. Dlatego deklaracja: unsigned long Wynik; odpowiada deklaracji przedstawionej poprzednio. 2.2 Operatory i słowa kluczowe Operatory służą do wykonywania operacji na zmiennych. Operacją w najprostszym przypadku może być działanie matematyczne na zmiennych, jednak operatory służą też do porównywania wartości, wskazywania na elementy złożonych struktur czy grupowania działań (nawiasy okrągłe). W języku C jest ustalone pierwszeństwo operatorów, czyli kolejność w jakiej operacje zostaną wykonane w przypadku braku nawiasów. Wszystkie operatory języka C zestawiono w tabeli 1. Część operatorów może z początku być niezrozumiała; ich przeznaczenie zostanie wyjaśniona na przykładach w następnych terminach zajęć. Niektóre operatory z tabeli są używane rzadko, i być może na zajęciach nie spotkasz się z koniecznością ich użycia. Operatory mogą być jednoargumentowe, czyli działające na pojedynczej zmiennej, lub wieloargumentowe - działające na wielu zmiennych. W praktyce wszystkie operatory wieloargumentowe poza jednym działają na dwóch zmiennych. Zauważ że występuje wiele operatorów zapisywanych takim samym znakiem, ale wykonujących różne działania w zależności od tego czy użyto je jako jedno- czy wieloargumentowe. Np. operator - może oznaczać odejmowanie (a-b) albo odwrócenie znaku zmiennej (-a), a operator służy do mnożenia (dwuargumentowy) lub pobrania wartości wskaźnika (jednoargumentowy). 5

Poza pierwszeństwem, określa się też łączność operatorów. Oznacza ona od której strony będą wykonywane operacje opisywane przez operatory o tym samym priorytecie. W przypadku łączności lewostronnej operacje wykonywane są od lewej strony. Przykładowymi operatorami łączonymi lewostronnie są operatory dodawania i odejmowania. Oznacza to, że działania zapisane a+b-c zostanie wykonany w kolejności (dodawanie, odejmowanie). Gdyby te operatory były łączone prawostronnie, działania zostałyby wykonane w kolejności (odejmowanie, dodawanie). Operatorami łączonymi prawostronnie jest np. operatory negacji bitowej i odwrócenia znaku. Przykładowo, zapis a oznacza, że najpierw zostanie wykonana negacja a następnie odwrócenie znaku. Szczególnym przypadkiem jest operator przypisania =. Każdy inny operator co prawda zwraca wartość, jednak normalnie nie jest ona przypisywana do niczego. Innymi słowy, zapis: a + b; jest poprawny z punktu widzenia języka, ale nic nie robi. Należy zapisać: Wynik = a + b; Operator przypisania jest łączony prawostronnie. Pozwala to na zastosowanie zapisu: Wynik = a = a + b; Ze względu na to że dodawanie ma pierwszeństwo wykonania przed przypisaniem, najpierw zostanie policzona suma. Następnie wynik zostanie przypisany do zmiennej a, a następnie nowa wartość zmiennej a zostanie przypisana do zmiennej Wynik. W razie potrzeby, kompletny zestaw reguł pierwszeństwa i łączenia operatorów języka C możesz znaleźć w internecie. 2.3 Funkcje Funkcje są elementami składowymi programu w języku C. Służą one grupowaniu często używanych fragmentów kodu. Szczególnym przypadkiem jest funkcja main, czyli główna funkcja programu. System operacyjny po załadowaniu programu zaczyna jego wykonanie przez wywołanie funkcji main. Oznacza to, że main jest jedyną funkcją która musi znaleźć się w każdym programie. Pozostałe funkcje mogą być nazwane dowolnie przez programistę, a ich nazwy podlegają takim samym ograniczeniom jak nazwy zmiennych. Nazwanie funkcji, podobnie jak dla zmiennej, ma postać deklaracji: int funkcja1(int); Powyżej zadeklarowano funkcję o nazwie funkcja1, która przyjmuje jedną zmienną typu int i zwraca jedną wartość typu int. Deklaracja musi znaleźć się w programie przed pierwszym użyciem funkcji, gdyż w przeciwnym wypadku kompilator zgłosi błąd. Poza deklaracją trzeba określić co dana funkcja ma robić, czyli napisać jej definicję: int funkcja1(int Indeks) kod funkcji Przy definicji trzeba nazwać zmienną przyjmowaną przez funkcję. Nazwa ta jest używana tylko w kodzie funkcji, i nie jest widoczna nigdzie indziej - czyli jest zmienną lokalną dla tej funkcji. Kod funkcji ma postać pojedynczej instrukcji złożonej. Należy pamiętać, że zamiast deklaracji funkcji można od razu napisać ich definicje. Innymi słowy, deklaracja nie jest obowiązkowa o ile definicja występuje przed pierwszym użyciem funckji. Czyli kod: int funkcja1(int Indeks) kod funkcji int main(void) 6

Nazwa operatora Znak Przykład operacji Opis Operatory arytmetyczne dodawanie + a + b Dodawanie arytmetyczne odejmowania - a - b Odejmowanie arytmetyczne mnożenia a b Mnożenie arytmetyczne dzielenia / a / b Dzielenie; dla liczb całkowitych wynik jest całkowity modulo % a % b Reszta z dzielenia liczb całkowitych minus - -a Odwraca znak zmiennej preinkrementacji ++ ++a Zwiększa wartość o 1 - tylko dla liczb całkowitych postinkrementacji ++ a++ Jak poprzedni, różni się pierwszeństwem predekrementacji -- --a Zmniejsza wartość o 1 - tylko dla liczb całkowitych Operatory logiczne logiczna negacja!!a Wynik zerowy dla a niezerowych i niezerowy dla zerowych logiczny iloczyn (AND) && a && b Zero jeżeli a lub b zerowe logiczna suma (OR) a b Zero jeżeli a i b zerowe Operatory bitowe bitowa negacja ~ ~a Wszystkie bity zmieniają wartość na przeciwną bitowy iloczyn & a & b Iloczyn logiczny liczony dla odpowiadających sobie bitów obu liczb bitowa suma a b Suma logiczna liczona dla odpowiadających sobie bitów obu liczb bitowy XOR ^ a ^b Bit wyniku zerowy jeżeli odpowiadające sobie bity operatorów różne przesunięcie w lewo << a <<b Bity w zmiennej a są przesuwane w lewo (w stronę najbardziej znaczącego) o b pozycji przesunięcie w prawo >> a >>b Bity w zmiennej a są przesuwane w prawo (w stronę najmniej znaczącego) o b pozycji Operatory przypisania przypisanie proste = a = b a przyjmuje wartość b przypisanie z dodawaniem += a += b Odpowiada operacjom a = a + b przypisanie z odejmowaniem -= a -= b Odpowiada operacjom a = a - b przypisanie z mnożeniem = a = b Odpowiada operacjom a = a b przypisanie z dzieleniem /= a /= b Odpowiada operacjom a = a / b przypisanie z modulo %= a %= b Odpowiada operacjom a = a % b przypisanie z iloczynem bitowym &= a &= b Odpowiada operacjom a = a & b przypisanie z sumą bitową = a = b Odpowiada operacjom a = a b przypisanie z XOR ^ a ^b Odpowiada operacjom a = a ˆb Operatory wskaźnikowe element tablicy [] a[b] wybiera b-ty element z tablicy a wskazanie a odwołanie do obiektu wskazywanego przez a referencja & &a adres (wskaźnik) do a element struktury. a.b element b struktury a element struktury (przez wskaźnik) -> a ->b jak wyżej, struktura dostępna przez wskaźnik a Operatory porównania równości == a == b Zwraca wartość niezerową jeżeli a równe b różnicy!= a!= b Zwraca wartość niezerową jeżeli a różne od b mniejszy < a <b Zwraca wartość niezerową jeżeli a mniejsze od b mniejszy lub równy <= a <= b Zwraca wartość niezerową jeżeli a mniejsze lub równe b większy > a >b Zwraca wartość niezerową jeżeli a większe od b większy lub równy >= a >= b Zwraca wartość niezerową jeżeli a większe lub równe b Operatory pozostałe grupowanie () (a + b) Działa jak nawias w arytmetyce - zmienia kolejność operacji wywołanie funkcji () a() Wywołuje funkcję o nazwie a przecinek, a, b Służy do oddzielania zmiennych w wywołaniu funkcji i przy deklaracjach warunkowy? : a : b? c Zwraca a jeżeli c niezerowe, b w przeciwnym wypadku rozmiaru sizeof sizeof(a) Zwraca ilość pamięci w bajtach zajmowaną przez zmienną a rozmiaru sizeof sizeof(typ) Zwraca ilość pamięci w bajtach zajmowaną przez dowolną zmienną typu (typ) rzutowanie () (typ)a Konwertuje a do typu (typ) Tablica 1: Operatory w języku C 7

Wynik = funkcja1(idx); działa tak samo jak kod: int funkcja1(int); int main(void) Wynik = funkcja1(idx); int funkcja1(int Indeks) kod funkcji W powyższych przykładach przy okazji pojawił się sposób wywołania funkcji. Używa się do tego operatora (), pisanego po nazwie funkcji. Wartość przesyłana do funkcji jest pisana w nawiasie, bez typu. Jeżeli wartości jest więcej, przy deklaracji, definicji i wywołaniu są one oddzielane przecinkami. Wartość zwracana z funkcji jest dostępna tak jak zmienna, i może być poddana dalszym operacjom. W powyższym przykładzie użyto przypisania do zmiennej Wynik, ale prawidłowe są też na przykład działania: Wynik = 2 * funkcja1(idx); Wynik2 += funkcja1(funkcja1(idx)); 2.3.1 Zmienne lokalne i globalne Wróćmy na chwilę do zmiennych. Teraz, gdy już wiesz o funkcjach, zapewne zauważyłeś że w kodzie zmienne można deklarować w dwóch miejscach: w funkcji lub poza nią. Jeżeli zmienna jest zadeklarowana wewnątrz funkcji: int funkcja1(int Indeks) int zmienna = 0; kod funkcji to jest to zmienna lokalna dla danej funkcji. Zmienna taka jest tworzona każdorazowo przy uruchomieniu funkcji i kasowana przy jej zakończeniu. Jest ona dostępna tylko podczas działania funkcji; dotyczy to również funkcji main(). Jak napisałem powyżej, parametry funkcji są jej zmiennymi lokalnymi. Oznacza to, że przy każdorazowym wywołaniu funkcji tworzona jest lokalna kopia przesyłanych do niej zmiennych. Dzięki temu dowolne operacje wykonane wewnątrz funkcji nie wpływają na wartość tych zmiennych w kodzie wywołującym funkcję. W powyższym przykładzie zmienna Idx nie ulegnie zmianie, niezależnie od tego co wewnątrz funkcji dzieje się ze zmienną Indeks. W przykładzie pojawiło się też słowo void. Mówi ono, że funkcja nie zwraca lub nie przyjmuje żadnych zmiennych. Jeżeli chcemy aby zmienna była dostępna tylko dla danej funkcji, ale nie była kasowana pomiędzy wykonaniami funkcji, możemy użyć modyfikatora static: int funkcja1(int Indeks) static int zmienna; kod funkcji 8

Zmienna statyczna jest dostępna tylko w funkcji, ale jej wartość jest zachowywana pomiędzy wywołaniami. Najprostszy przykład zastosowania takiej zmiennej to licznik wywołań danej funkcji. Zauważ że w powyższym kodzie nie ma przypisania do zmiennej przy deklaracji. To dlatego, że zmienne statyczne mają automatycznie przypisywaną wartość 0. Jeżeli zmienna została zadeklarowana poza jakąkolwiek funkcją, to jest to zmienna globalna. Zmienna taka jest widziana przez wszystkie funkcje i może być przez nie modyfikowana, przy czym obowiązują tutaj takie same reguły jak dla deklaracji funkcji. Z tego powodu zmienne globalne zwykle deklaruje się na początku pliku z kodem programu. Dodatkowo zmienna globalna ma przy utworzeniu (czyli na starcie programu) automatycznie przypisywaną wartość 0 (podobnie jak zmienna statyczna). Zmienne globalne są wykorzystywane do wymiany danych pomiędzy funkcjami programu, jednak praktycznie wszystkie zestawy reguł programowania zalecają ograniczenie ich stosowania do niezbędnego minimum, gdyż nadmiar zmiennych globalnych stwarza duże zagrożenie przypadkowego zmodyfikowania danych. Ostatnią kwestią dotyczącą zmiennych jest tzw. przysłanianie. W rozdziale o zmiennych napisałem że zmienne powinny mieć unikalne nazwy. Otóż dotyczy to tylko zmiennych lokalnych dla danej funkcji; inna funkcja może mieć dokładnie tak samo nazwane zmienne lokalne, gdyż i tak są one widoczne tylko podczas jej wykonywania. Zmienne globalne również powinny mieć unikalne nazwy. Jeżeli jednak w funkcji wystąpi zmienna lokalna o takiej samej nazwie jak jedna ze zmiennych globalnych, to w tej funkcji widoczna będzie tylko zmienna lokalna a zmienna globalna będzie niedostępna. Mówimy wtedy że zmienna globalna została przysłonięta przez zmienną lokalną. Z tego powodu zmienne globalne zwykle wyróżnia się nadając im charakterystyczne nazwy nie wykorzystywane dla zmiennych lokalnych. Przykładowym rozwiązaniem jest zapis nazw zmiennych lokalnych małymi literami, a zmiennych globalnych dużymi. 2.4 Wyrażenia warunkowe Wyrażenia warunkowe służą kontroli działania programu, umożliwiając podejmowanie działań w zależności od wartości zmiennych. Poza opisanym wcześniej operatorem warunkowym?:, w języku C są dwa inne rodzaje wyrażeń warunkowych. Są to konstrukcje ifelse oraz switchcase. Pierwsza z nich ma postać: if(warunek) instrukcja lub instrukcja złożona (w klamrach) else instrukcja lub instrukcja złożona Działa ono w następujący sposób: najpierw obliczana jest wartość wyrażeń tworzących warunek. Może to być po prostu zmienna, ale może to też być np. odwołanie do funkcji. Jeżeli wartość ta jest niezerowa, wykonywana jest instrukcja znajdująca się bezpośrednio po wyrażeniu if(warunek). W przeciwnym razie wykonywana jest instrukcja prosta lub złożona znajdująca się po wyrażeniu else, przy czym wyrażenie to wraz z odpowiadającą mu instrukcją nie musi występować. Dopuszcza się zagnieżdżanie wyrażeń ifelse: if(warunek1) instrukcja złożona else if(warunek2) instrukcja złożona else instrukcja złożona Drugim typ konstrukcji warunkowej, switchcase, ma postać: switch(wyrażenie) case wartość1: kod1 break; case wartość2: kod2 break; default: 9

kodn break; Na początku określana jest wartość wyrażenia wyrażenie, przy czym musi ona być liczbą całkowitą; niedopuszczalne są liczby zmiennoprzecinkowe czy tekst. Następnie wykonywany jest kod znajdujący się pomiędzy słowem case z odpowiednią wartością, a najbliższym słowem break. Oznacza to, że można zrobić taką konstrukcję: switch(wyrażenie) case wartość1: case wartość2: kod do wykonania gdy wyrażenie == wartość1 lub wartość2 break; case wartość3: kod do wykonania gdy wyrażenie == wartość3 case wartość4: kod do wykonania gdy wyrażenie == wartość3 lub wartość4 break; default: kod do wykonania gdy wyrażenie nie jest równe żadnej z wymienionych wartości break; Kod wpisany po słowie default jest wykonywany gdy wartość wyrażenia nie odpowiada żadnej instrukcji case. Default nie jest obowiązkowe. 2.5 Pętle Pętle służą do wielokrotnego powtarzania wykonania wybranego kodu, przy czym ilość powtórzeń może ale nie musi być znana w momencie rozpoczęcia pętli. W języku C istnieją trzy konstrukcje służące do realizacji pętli: for(;;), while() oraz dowhile(). Konstrukcja for(;;) jest używana najczęściej do realizacji pętli o z góry znanej liczbie wykonań. Ma ona postać: for(inicjalizacja; warunek zakończenia; krok) instrukcja prosta lub złożona Przed rozpoczęciem pętli wykonywana jest inicjalizacja, następnie następuje sprawdzenie warunku zakończenia. Jeżeli jest on spełniony (wartość niezerowa), wykonanie pętli na tym się kończy. W przeciwnym wypadku wykonywana jest instrukcja pętli, a następnie krok. Później następuje sprawdzenie warunku zakończenia i cały cykl się powtarza. Dobrze to ilustruje przykład: for(l=0; L<10; L++) instrukcja W którym zmienna L służy jako licznik iteracji, na początku ustawiany na zero, porównywany z wartością 10 i inkrementowany po każdym wykonaniu instrukcji. Taka pętla wykona się 10 razy (dla wartości L od 0 do 9). Warto zauważyć, że pętla for może nie wykonać się ani razu, jeżeli warunek będzie od razu spełniony. Dopuszczalne jest też pominięcie dowolnej części pętli, czyli: for(l=0; L<10; ) instrukcja zakłada że L zmienia się gdzieś indziej (np. jest zmieniane przez instrukcję), a for(;;) instrukcja 10

będzie wykonywać instrukcję aż do wyłączenia programu. Konstrukcja while() najczęściej służy do realizacji pętli których warunek zakończenia nie jest z góry znany i które mogą nie wykonać się ani razu. Ma ona postać: while(warunek) instrukcja prosta lub złożona W pętli tej najpierw sprawdzany jest warunek. Jeżeli wynik sprawdzenia jest niezerowy, następuje wykonanie instrukcji. Cykl powtarza się dopóki warunek nie będzie zerowy. W odróżnieniu od pętli for, w tej pętli nie ma inicjalizacji ani kroku; powinny one być realizowane przez programistę przed pętlą (inicjalizacja) oraz wewnątrz niej (krok). Konstrukcja dowhile() jest podobna do while(), jednak warunek zakończenia znajduje się na końcu: do instrukcja prosta lub złożona while(warunek) W tym przypadku najpierw wykonywana jest instrukcja, a następnie sprawdzany warunek. Oznacza to, że pętla tego rodzaju zawsze wykona się co najmniej raz, nawet jeżeli warunek jest od razu zerowy. 2.6 Dyrektywy preprocesora Dyrektywy preprocesora służą sterowaniu jego działaniem. Jest ich wiele, ale dwie najczęściej spotykane to #include oraz #define. Dyrektywa #include służy dołączaniu do pliku źródłowego plików nagłówkowych. Ma ona postać: #include <nazwapliku.h> lub #include "nazwapliku.h" W pierwszym przypadku plik nagłówkowy jest poszukiwany w ścieżce systemowej (dołączamy bibliotekę), a w drugim tylko w ścieżce lokalnej (dołączamy własny plik nagłówkowy). Dołączony plik nagłówkowy jest przetwarzany przez preprocesor natychmiast po dołączeniu. Dyrektywa #define służy przypisywaniu wartości dowolnie wybranym nazwom. Ma ona postać: #define nazwa wartość We wszystkich liniach poniżej tej dyrektywy nazwę i wartość można stosować zamiennie. 3 Zadania 3.1 Zadanie 1 Napisz program, który wypisze informacje o rozmiarze wszystkich znanych Ci typów danych. Zrealizuj to na dwa sposoby: poprzez użycie operatora sizeof z argumentem będącym typem danych, oraz z argumentem będącym zmienną danego typu. Wykorzystaj gotowy program rozmiary.c (dołączony do instrukcji), z przykładowym wywołaniem funkcji bibliotecznej printf. 3.2 Zadanie 2 Na podstawie programu przykładowego petla1.c (dołączony do instrukcji) napisz program, który będzie wypisywał kolejne liczby (nieujemne) w kolejnych liniach, ale tak aby przed każdą liczbą występowało tyle spacji ile wynosi wypisana liczba. (przykładowo przed liczbą zero - zero spacji, przez jedynką jedna spacja itp.): 0 1 2 3 11

3.3 Zadanie 3 Zmodyfikuj program z zadania 3 tak, aby korzystał z pętli while. 12