jezyk C skrypt cz 1

background image

I. Metody i techniki programowania komputerów

1. Wstęp

Celem zajęć laboratoryjnych z przedmiotu „Informatyka” jest przekazanie

przyszłemu inżynierowi umiejętności korzystania w jego pracy z możliwości

programowania komputerów i tworzenia własnych procedur obliczeniowych.

Ucząc się programowania należy opanować zarówno język programowania (np.

język C) jak i zapoznać się z logiką programowania z użyciem komputera.

Poznanie języka programowania (lub nawet kilku) nie wystarczają do

samodzielnego opracowania programu, jeżeli nie opanowało się odpowiednich

metod i technik programowania.

Programowanie jest procesem, w ramach którego muszą być zrealizowane

następujące czynności:

a) analiza otrzymanego przez programistę zadania,

b) poszukiwanie i wybór odpowiedniej metody programowania,

c) opracowanie algorytmu opisującego sposób wykonania przez komputer

zleconej pracy,

d) zakodowanie algorytmu zapewniające jego realizację przez komputer, tzn.

zakodowanie go w wybranym języku programowania i uzyskanie w ten

sposób tzw. programu źródłowego,

e) przetłumaczenie programu źródłowego na wewnętrzny język komputera,

a tym samym otrzymanie programu roboczego,

f) sprawdzenie poprawności programu i poprawienie wykrytych błędów,

g) opracowanie opisu (tzw. dokumentacji) programu.

Od programisty wymagana jest więc nie tylko wiedza w zakresie zapisywania

we właściwy sposób poleceń dla komputera, lecz także umiejętność

konstruowania algorytmów.

background image

2. Analiza zadania programistycznego

Wstępna analiza zadania programistycznego powinna doprowadzić do

wypracowania odpowiedzi na następujące pytania:

a) jakie dane wejściowe podlegają opracowaniu: jaka jest ich struktura logiczna

i fizyczna, na jakim nośniku są zapisane, jaki jest ich rodzaj i ilość ? Czy

mogą wystąpić błędy zakłócające proces przetwarzania danych i czy w

związku z tym trzeba podjąć odpowiednie kroki w celu wykrycia takich

błędów? Jakie wartości ekstremalne mogą przyjmować dane wejściowe ?

b) jakie wyniki, w jakiej postaci i ilości oraz na jakim nośniku musimy

dostarczyć odbiorcy ? Jaka powinna być dokładność tych wyników?

c) Jakie ograniczenia i uwarunkowania musimy uwzględnić podczas

programowania ? Które z nich trzeba traktować jako warunki konieczne, a

które – jako dodatkowe, pożądane ?

d) Czy może być potrzebna jakaś pomoc ? Czyja i w jakim zakresie ?

Ad. a)

Najczęściej

początkujący

programiści

zakładają,

ż

e

podczas

przetwarzania maszynie zostają dostarczone wyłącznie dane poprawne,

spełniające wszystkie stawiane w tym zakresie warunki i wymagania. W

konsekwencji takiego założenia brak jest w większości programów rozwiązań,

które zapobiegną uzyskaniu błędnych wyników w wypadku odczytania błędnej

danej. Należy więc przyjmować zasadę ograniczonego zaufania do ich

poprawności, a zatem przewidywać konieczność sprawdzania danych

wejściowych ze względu na te operacje, które w szczególny sposób mogą

zaważyć na działaniu programu, gdyby w owych danych znalazł się błąd nie

wykryty wcześniej.

Nieuwzględnienie czynnika ilości danych wejściowych i wyników

przetwarzania może spowodować, że zostanie opracowany program, którego

background image

wykorzystanie w praktyce jest skomplikowane lub wręcz niemożliwe z

powodów technicznych. Jeżeli przetwarzaniu ma ulec duża ilość danych

wejściowych, to proces ten może trwać bardzo długo i należy liczyć się z

możliwością awarii sprzętu lub błędu człowieka, co zazwyczaj prowadzi do

konieczności powtórzenia całej pracy. W celu wyeliminowania tego zagrożenia

powinno

się

przewidzieć

w

programie

odpowiednie

rozwiązania

zabezpieczające.

Poznanie danych wejściowych powinno polegać na ustaleniu:

- treści (zawartości) informacyjnej tych danych,

- źródeł ich pochodzenia,

- procesu, w wyniku którego zostały one przygotowane do przetwarzania,

- ich jakości (czy były podejmowane działania w celu wykrycia w nich

ewentualnych błędów i jaki był zakres takiej kontroli),

- rozmiarów zbiorów danych i nośnika na którym zostały zapisane,

- struktury zbiorów danych (typów tzw. rekordów, treści danych zawartych w

każdym takim typie, budowy każdego typu rekordu, układu rekordów w

poszczególnych zbiorach).

Ad b)

Zbiory danych wyjściowych muszą być zebrane i zapisane wg. kryteriów

ustalonych w podobny sposób jak dane wejściowe. Trzeba jednak zauważyć, że

w tym wypadku programista ma zwykle większą swobodę w ustalaniu struktury,

treści i sposobu zapisu tych zbiorów, aniżeli w odniesieniu do danych

wejściowych. Przykładowo, jeżeli użytkownik życzy sobie, by wyniki były

drukowane na papierze to wymaganie to powinno być spełnione, a margines

swobody jaki pozostaje programiście może dotyczyć np. takiego sposobu

wydruku, aby zapisy były jak najbardziej czytelne.

background image

Ad c)

Jednym z ważnych warunków ograniczających, jaki programista musi

uwzględnić, jest czas, w którym program ma być opracowany. Na przykład,

poprzez wybór określonej metody programowania lub określonego języka

programowania można uzyskać rozwiązanie, które w końcowym efekcie okaże

się korzystne ze względu na stosowany sprzęt i czas pracy. Jeżeli jednak terminy

są krótkie, to programista musi skupić całą uwagę na uzyskaniu rozwiązania w

czasie satysfakcjonującym użytkownika, nawet kosztem doskonałości programu.

Innym ograniczeniem mogą być żądania odnośnie zajmowanego przez program

obszaru pamięci operacyjnej i innych urządzeń zewnętrznych, czasu realizacji

programu przez komputer, możliwości interwencji ze strony człowieka w

przebieg pracy komputera przy wykonywaniu programu.

3. Elementarne wiadomości o algorytmach

Algorytm jest to termin określający pewien proces obliczeniowy jako zespół

precyzyjnych i jednoznacznych reguł działania. W informatyce algorytmem

nazywa się przepis określający skokowy proces przekształcania danych

wejściowych w wymagane wyniki. Algorytm musi spełniać następujące

warunki:

a) warunek jednoznaczności, tzn. musi jednoznacznie określać rodzaj i

kolejność operacji, jakie mają być realizowane przez komputer,

b) warunek masowości, tzn. musi charakteryzować się dużym stopniem

uogólnienia, aby umożliwiał rozwiązywanie różnych wariantów danego

zadania,

c) warunek skuteczności, tzn. algorytm musi prowadzić do uzyskania

rozwiązania zadania w skończonym czasie.

background image

W strukturze algorytmu zawsze występują dwa typy jego elementów

konstrukcyjnych: operacje oraz dane, na których te operacje są wykonywane.

Dane można podzielić na dwa rodzaje:

a) dane stałe, tzn. liczby, symbole, teksty podawane w algorytmie w postaci

jawnej,

b) dane zmienne, tzn. takie wartości, które nie są znane lub nie mogą być

skonkretyzowane podczas opracowywania algorytmu.

Dane zmienne w algorytmie oznacza się pewnymi symbolami literowo –

cyfrowymi. Oznaczenia takie nazywa się nazwami zmiennych. Operacje, z

których budowany jest algorytm powinny mieć charakter czynności

elementarnych wykonywanych przez komputer. Przed konstruowaniem

algorytmu należy zdefiniować pewien zbiór operacji elementarnych, z których

można skorzystać. Zbiór taki może być określony z zewnątrz np. w postaci listy

rozkazów dostępnych w wybranym języku programowania.

Opis każdej operacji elementarnej złożony jest z pewnego kodu i operandu. Kod

służy do zidentyfikowania operacji, a także może być wykorzystany jako jej

oznaczenie, symbol. Operand operacji określa dane, które biorą udział w

przetwarzaniu oznaczonym przez funkcje rozpatrywanej operacji, lub inne

wielkości, które muszą wystąpić, by operacja mogła być poprawnie wykonana.

W związku z tym, poznanie każdej operacji polega na ustaleniu funkcji, jakie

ona wywołuje, oraz danych, które przetwarza, i wyników, do których prowadzi.

4. Proces budowania algorytmu

Praca nad zbudowaniem algorytmu dla danego zadania może być rozłożona

na pewne etapy. W pierwszym etapie można szkicować całe rozwiązanie w

ogólnym zarysie posługując się przy tym własną wiedzą i umiejętnościami, w

ten sposób, jak przygotowuje się instrukcję pracy dla człowieka. Następnie po

upewnieniu się, że opracowana wersja algorytmu jest merytorycznie poprawna,

background image

modyfikuje się ją tak, aby wyeliminować operacje, których komputer nie może

wykonać, zastępując je przez operacje elementarne, np. dostępne w zbiorze

instrukcji danego języka programowania. Przekształcenie wstępnej wersji

algorytmu wiąże się z koniecznością uzupełnienia go o pewne operacje

dodatkowe, związane z tym, ostatecznie będzie on realizowany przez komputer.

Do operacji tych zaliczyć należy:

a) odczytywanie danych wejściowych z nośników technicznych,

b) wyprowadzania wyników przetwarzania na urządzenia zewnętrzne,

c) przerwania pracy komputera po zakończeniu rozwiązywania zadania,

d) przygotowania niezbędnych wartości początkowych zmiennych biorących

udział w tych operacjach arytmetycznych, których wynik zależy od tego, jaką

wartość te zmienne miały przed rozpoczęciem a wykonywania

5. Prezentacja algorytmów, schematy blokowe

W celu przejrzystego przedstawienia algorytmu zamiast słownych opisów

stosowane są rysunki, szkice oraz tzw. schematy blokowe. Schemat blokowy to

układ płaskich figur geometrycznych połączonych z sobą odcinkami prostych,

łamanymi lub krzywymi. Występujące w nim figury geometryczne noszą nazwę

skrzynek, natomiast łączące je linie nazywa się ścieżkami sterującymi. Skrzynki

służą do przedstawienia działań zaprojektowanych w algorytmie, zaś linie

wskazują kolejność wykonywania tych działań.

Główną zaletą schematów blokowych jest to, że graficznie prezentują one

algorytm zarówno od strony występujących w nim działań, jak i ich kolejności.

Każda skrzynka w schemacie blokowym prezentuje określony rodzaj operacji.

Wykaz stosowanych w schematach blokowych symboli przedstawia tablica nr 1.

background image

Tablica 1 .Wykaz symboli stosowanych w schematach blokowych

Symbol

Znaczenie symbolu

Operacje, w wyniku których ulega zmianie wartość

danych w pamięci komputera

Operacja wprowadzania danych do pamięci operacyjnej

komputera

Operacja wyprowadzania danych z pamięci operacyjnej

komputera

Operacja warunkowa dotycząca konieczności wyboru

jednej z dróg: związanej z spełnieniem (T) lub nie

spełnieniem (N) zapisanego warunku

moduł

cykliczny

(pętla),

w

którym

operacje

wykonywane są wielokrotnie, (np. cykl powtarza się,

dopóki zmienna „i” przyjmująca wartości od 1, z

krokiem 1, nie osiągnie wartości n)

Proces określony poza danym algorytmem (podprogram)

nie wymagający zdefiniowania w rozpatrywanym

schemacie blokowym

Określenie kierunku przepływu danych lub kolejności

wykonywania działań

background image

Łączenie dróg przepływu danych lub kierunków

wykonywania działań, kiedy drogi są z sobą związane

logicznie

Łącznik stronicowy (etykieta), wskazujący wejście lub

wyjście z wyodrębnionych fragmentów schematu

znajdujących się na jednej stronie (arkuszu papieru)

Łącznik międzystronicowy, wskazujący wejście lub

wyjście z wyodrębnionych fragmentów schematu

rozmieszczonych na różnych stronach

Oznaczenie miejsca rozpoczęcia działania programu

Oznaczenie miejsca zakończenia lub przerwania

działania programu

Oznaczenie miejsca komentarza

background image

II. Opis języka programowania C/C++

1. Historia powstania i rozwoju języka C.

Język C został opracowany w połowie lat siedemdziesiątych. Jego

autorami byli Brian. W. Kernighan i Dennis M.Ritchie z Bell Telephone Labs,

gdzie powstał również system operacyjny UNIX. Dzięki pełnemu, jasnemu

opisowi języka w książce „The C Programming Language” w krótkim czasie

opracowano kompilatory dla wielu różnych komputerów. Od tej pory nastąpiło

jednak wiele zmian w języku C, których nie obejmował standard stworzony

przez Kernighana i Ritchie. W trosce o to aby C nie utracił sowej

uniwersalności grupa producentów i projektantów oprogramowania zwróciła

się do instytutu ANSI (American National Standard Institute) z petycją o

ustanowienie standardu dla tego języka programowania. Amerykański

Narodowy Instytut Standaryzacji przyjął petycję i utworzył komitet techniczny

X3J11 Technical Committee w celu opracowania standardu. Pod koniec roku

1989 prace zostały zakończone i opracowano standard języka C. Został on

opublikowany pod tytułem „Draft Proposed American National Standard –

Programming Language C (1984r). Standard ten różnił się nieco od propozycji

autorów języka. Dla odróżnienia obu wersji przyjęto oznaczenie K&R C dla

wersji autorskiej i ANSI C dla standardu ANSI.

ANSI C podobnie jak pierwowzór, jest jednak językiem umożliwiającym

tylko programowanie proceduralne. Wraz z rozwojem obiektowych elementów

języka powstał standard AT&T Bell Laboratories, który obejmuje oba rodzaje

elementów języka. Na początku lat osiemdziesiątych Bjarne Stroustrup

opracował język programowania C++ w Laboratoriach Bella firmy AT&T.

Język ten powstał jako rozwinięcie języka programowania C , który

rozszerzono w trzech ważnych kierunkach:

background image

1. stworzono narzędzia pozwalające definiować i stosować abstrakcyjne

typy danych,

2. stworzono narzędzia projektowania,

3. wprowadzono wiele subtelnych ulepszeń do istniejących konstrukcji

języka C,

Język C++ jest już szeroko dostępny i często służy do pisania rzeczywistych

programów użytkowych i rozbudowania systemów. W 1988 roku

wyprodukowano pierwsze oryginalne kompilatory dla rynku oprogramowania

dla komputerów PC i stacji roboczych. Ponadto zaczęły się pojawiać duże

biblioteki programów i powstawało środowisko programistyczne języka C++.

2. Ogólne zasady pisania i uruchamiania programów

Tworzenie programu w C można podzielić na 4 etapy:

a) Napisanie kodu źródłowego w dowolnym edytorze tekstu

Kod źródłowy programu to tekst stanowiący pewien ciąg instrukcji dla

komputera zapisanych zgodnie z pewnymi zasadami określonego języka

programowania. Rozróżniane są języki tzw. „wysokiego poziomu” (w tym

m.in. język C) oraz języki wewnętrzne tzw. języki „niskiego poziomu”. Aby

wyjaśnić te pojęcia należy najpierw zrozumieć zasadę działania

mikroprocesorów komputerów.

Mikroprocesor, czyli układ scalony sterujący wszystkim co zachodzi w

komputerze, z technicznego punktu widzenia może wykonywać tylko cztery

operacje. Może przenieść dane z jednego miejsca pamięci do drugiego,

zmienić dane w podanym miejscu pamięci, sprawdzić, czy wskazane miejsce

w pamięci zawiera wybrane dane oraz zmienić kolejność wykonywanych

czynności. Wszystkie te działania są wykonywane za pomocą wysyłania,

otrzymywania lub sprawdzania impulsów elektronicznych. Impulsy mogą

background image

znajdować się tylko w jednym z dwóch stanów (określanych przez poziom

napięcia) : ączony lub wyłączony. Aby zapisać instrukcje komputerowe na

tym bardzo podstawowym poziomie, korzysta się z 0 jako przedstawiciela

stanu wyłączonego oraz z 1, która oznacza włączenie. Nazywa się cyframi

binarnymi (bitami) lub instrukcjami binarnymi, opartymi na systemie

dwójkowym, w którym do przedstawienia wszystkich liczb wykorzystuje się

kombinację tylko zer i jedynek. W miarę rozwoju komputerów opracowano

język asemblerowy korzystający z kodów mnemonicznych do przedstawiania

zadań odnoszących się bezpośrednio do mikroprocesora. Każdy kod (np.

MOV – przesuń) przedstawia osiem lub więcej instrukcji binarnych.

Assembler przekłada te kody na pojedyncze sygnały elektroniczne, a

ponieważ każdy kod odnosi się bezpośrednio do wewnętrznych funkcji

mikroprocesora, assembler nazywany jest językiem programowania niskiego

poziomu.

Obecnie korzysta się z języków wysokiego poziomu, którego instrukcjami są

słowa zrozumiałe dla ludzi, a nie proste słowa mnemotechniczne. Każde

słowo przedstawia zwykle kompletną operację, a nie jedno zadanie dla

mikroprocesora. Języki programowania wysokiego poziomu są łatwe w

obsłudze, czytelne i łatwe do przenoszenia pomiędzy komputerami różnych

typów.

Przed wykonaniem danej instrukcji języka wysokiego poziomu (np. języka

C) należy ją przetłumaczyć na ciąg instrukcji binarnych. Proces ten

nazywany jest kompilacją.

b) Kompilacja kodu źródłowego

Tekst programu zapisany w pliku źródłowym o nazwie zakończonej zwykle

rozszerzeniem .c zostaje przetłumaczony na format dwójkowy (binarny).

Czynność tą wykonuje kompilator, program komputerowy, który dodatkowo

sprawdza, czy tekst źródłowy programu zgadza się z regułami języka C. Jeśli

background image

kompilator wykryje błędy w tekście źródłowym programu, sygnalizuje to

odpowiednimi komunikatami. Komunikaty te pozwalają odnaleźć miejsce

wystąpienia błędu i poprawić go powracając do edycji pliku źródłowego.

Po poprawnym skompilowaniu programu do postaci tzw. modułu

relokowalnego (pliku wynikowego) kompilator wywołuje program linkera w

celu stworzenia programu wykonywalnego. W systemie UNIX takie pliki

zazwyczaj ma rozszerzenie *.o, w systemie DOS natomiast * .OBJ.

c) Łączenie za pomocą programu „linker”

Plik binarny skompilowany jest już przetłumaczony na 0 i 1, ale jeszcze nie da

się go uruchomić, ponieważ brak mu niektórych funkcji i pewnych fragmentów

kodu. Program typu linker (konsolidator) jest programem łączącym wszystkie

moduły programu z modułami bibliotecznymi w jeden program wykonywalny.

Do skompilowanego pliku programu użytkownika dodany zostaje z bibliotek

binarny kod gotowych funkcji i całość jest zapisywana na dysku w nowym pliku

– programie gotowym do wykonania. W systemie DOS takie pliki mają

rozszerzenie *.EXE. Pliku wykonywalnego nie można przenosić pomiędzy

komputerami różnych typów i z różnymi systemami operacyjnymi, nawet jeśli

sam język ANSI C, w którym powstał kod źródłowy jest w pełni przenośny.

d) Uruchomienie programu.

Ostatni etap pisania programu polega na jego testowaniu i usuwaniu błędów.

Pomimo usuwania błędów formalnych w programie przez kompilator i

konsolidator, często zdarza się, że wyniki programu są inne niż oczekiwano.

ędy wykonania występują wówczas, gdy program zawiera instrukcję, której

nie może wykonać. Błędy wykonania dotyczą zwykle plików lub urządzeń

zewnętrznych. Błędy logiczne występują wówczas, gdy program postępuje

zgodnie z instrukcjami, ale za to instrukcje są błędne, co oznacza, że dają błędne

wyniki. Są to problemy najtrudniejsze do wykrycia, ponieważ programista może

background image

nawet nie wiedzieć o ich istnieniu. Aby wyszukać takie „ukryte” błędy można

wykorzystać tzw. debuggera (dosł. „odpluskwiacza”). Debugger pozwala

wykonywać program krokowo – wiersz po wierszu – a programista może

uważnie prześledzić, co się dzieje w programie, mogąc zatrzymać program w

każdej chwili.

Po usunięciu błędów wykonania lub błędów logicznych należy ponownie

program skompilować i skonsolidować.

3. Struktura programu

Program napisany w języku C składa się z definicji funkcji oraz z deklaracji

funkcji i zmiennych. Każdy program musi zawierać definicję funkcji o nazwie

main, a wykonanie programu rozpoczyna się od tej właśnie funkcji. Najkrótszy

program będzie miał zatem postać;

main()

{

}

W programie tym main jest nazwą funkcji, nawiasy krągłe obejmują pustą listę

parametrów funkcji, a para nawiasów klamrowych stanowiących ciało (treść)

funkcji nie zawiera ani jednej instrukcji. Wykonanie takiego programu

oczywiście nie wywołuje żadnych skutków. Nawiasy okrągłe występujące po

nazwie funkcji main są częścią tej nazwy i są niezbędne. Informują one

kompilator, że odnosimy się do funkcji a nie do jakiegoś słowa main. Nawiasy

klamrowe

{}

oznaczają początek i koniec bloku lub sekcji kodu programu.

Każda wpisywana funkcja musi zaczynać się i kończyć nawiasami klamrowymi.

Pomiędzy nimi wpisywane są instrukcje, które mają być wykonane przez

komputer w celu realizacji określonego zadania.

background image

Przykładem programu, który wywoła już jakiś określony efekt jest ten, od

którego zaczynał naukę prawie każdy programista:

/* Moj pierwszy program */

#include <stdio.h>

main()

{

puts(”Witaj!”);

return 0;

}

W programie tym jak poprzednio występuję tylko jedna funkcja: main, ale

pojawiła się w niej instrukcja puts(”Witaj !”) oraz funkcja return. Zapis

instrukcji puts(”Witaj !”) powoduje wyświetlenie na ekranie monitora napisu

„Witaj !”. W instrukcji tej wykorzystujemy jedną z wielu napisanych i

skompilowanych funkcji znajdujących się w bibliotece funkcji dostarczanych

wraz z kompilatorem. Zakres pełnej biblioteki tych funkcji został określony

przez komitet Amerykańskiego Narodowego Instytutu Normalizacji (ANSI).

Stosując zapis puts(”Witaj !”) nakazujemy wykonanie instrukcji stanowiących

standardową funkcję puts(). Dla funkcji tej instrukcje nakazują umieszczenie na

ekranie określonego napisu. Treść napisu zostaje przekazana funkcji jako

parametr zapisany pomiędzy nawiasami okrągłymi. Znaki cudzysłowu

wskazują, że wyświetlony będzie łańcuch liter tworzących napis Witaj !, a nie

zmienna lub stała.

Funkcja return zapisana w ostatniej linii programu przed znakiem }

nakazuje komputerowi powrót do systemu operacyjnego. W przypadku

większości kompilatorów języka C polecenie to jest opcjonalne. Jeśli go nie

będzie nie pojawi się komunikat o błędzie.

Funkcje w języku C, w tym także funkcja main () mogą zwracać wartość

(wynik). Zapis return 0 oznacza, że program wykonał się prawidłowo i do

końca. Wartość różna od zera zwrócona przez program do systemu

background image

operacyjnego oznacza, że wystąpiły błędy. Im większa zwrócona wartość, tym

poważniejszy błąd. W kompilatorach C spełniających standard ANSI trzeba

określić typ funkcji nawet jeśli funkcja nie oblicza wartości. W tym przypadku

deklaruje się typ void – czyli pusty, np.

void mojafunkcja ()

W ten sposób kompilator otrzymuje informację, że funkcja nie zwraca żadnej

wartości do wywołującej ją funkcji. Jeżeli funkcja ma zwrócić jakąś wartość,

trzeba wskazać typ zwracanej wartości. Robimy to określając typ przed nazwą

funkcji (deklarując typ funkcji), np.:

float oblicz() /*float – to liczba rzeczywista*/

Zwracana wartość jest argumentem funkcji return(). Zdefiniowane w języku C

typy wartości funkcji będą omówione w dalszej części skryptu.

Nie wyjaśniony został jeszcze zapis w pierwszej (po komentarzu) linii

programu. Wiersz ten rozpoczyna się od znaku #(hash) po którym następuje

słowo include (dołącz). Polecenie to jest dyrektywą nakazującą kompilatorowi

wykorzystanie informacji zawartych w pliku nagłówkowym o nazwie stdio.h.

Nazwa stdio jest skrótem od słów standard input/output (standardowe wejście-

wyjście), a plik stdio.h zawiera instrukcje potrzebne kompilatorowi do pracy z

plikami na dysku i do wysyłania informacji do drukarki.

Nazwa pliku stdio.h jest w przykładzie ujęta w nawiasy < >. Oznacza to,

ż

e pliku nagłówkowego należy poszukiwać w katalogu innym niż bieżący.

Kompilator w takim przypadku rozpocznie szukanie tego pliku od

standardowego katalogu o nazwie include. Jeżeli chcemy aby kompilator szukał

pliku nagłówkowego w katalogu bieżącym to należy nazwę pliku

nagłówkowego ująć w cudzysłów:

#include ”stdio.h”

Katalogi zawierające pliki nagłówkowe są instalowane standardowo podczas

instalacji kompilatorów C/C++ na dysku użytkownika. Przykładowo, jeśli

background image

zainstalowany został kompilator w katalogu C:\BORLANDC, to pliki

nagłówkowe będą znajdować się w katalogu C:\BORLANDC\INCLUDE.

Zapisując nazwy plików, funkcji lub zmiennych należy zwracać uwagę na

stosowanie małych i dużych liter. Język C rozróżnia małe i duże litery, czyli

inaczej mówiąc jest wrażliwy na wielkość liter. Dla przykładu: stdio.h i

STDIO.H to dla C dwie różne nazwy. Podobnie main() i Main() to dwie różne

nazwy dwóch różnych funkcji.

Programy w języku C mają format swobodny, co oznacza, że nie jest

istotny sposób zapisu programu, a jedynie kolejność występowania

poszczególnych jego elementów. Przytoczony więc na początku rozdziału

program można zapisać następująco:

main () {}

Pisząc program ważne jest jednak, aby był on zapisany w sposób czytelny i

przejrzysty. Program napisany przejrzyście łatwiej jest przeczytać, zrozumieć i

poprawić. Wskazane jest uzupełnianie programu komentarzami. Komentarz w

języku C, to dowolnej treści tekst ograniczony znakami

/*

i

*/

. Przedstawiony

powyżej program z objaśnieniami w formie komentarzy mógłby wyglądać tak:

/**********************************/

* Najprostszy program w jezyku C *

**********************************/

main() /* glowna funkcja bez argumentow */

{

/* pusty blok funkcji */

}

/* koniec programu*/

background image

Programiści zwykle umieszczają komentarze na początku programu, aby

wyjaśnić jego ogólne zadanie. Komentarze pojawiające się wewnątrz programu

objaśniają wybraną instrukcję lub zastosowane działanie. Jeśli instrukcja i

komentarz znajdują się w tym samym wierszu, to zwyczajowo zostawia się

między nimi pewien odstęp. Ułatwia to czytanie zarówno instrukcji jak i

komentarza.

Wraz z opracowaniem C++ pojawiła się metoda poprzedzania tekstu

komentarza (ale tylko pojedynczego wiersza) podwójnym ukośnikiem. Pisząc

programy w Visual C++ i w Borland C++ dopuszczalny jest więc taki zapis:

// Komentarz ...

Należy zauważyć, że taki sposób zapisu komentarzy nie został włączony do

standardu ANSI C i przed jego stosowaniem powinno się sprawdzić czy

wybrany kompilator uznaje tą konwencję.

Dla czytelności programów napisanych w języku C przyjmuje się pewne

konwencje, których stosowanie nie jest konieczne, ale świadczy o staranności

autora o takie przedstawienie tekstu programu aby był on zrozumiały dla innych.

W zakresie struktury programu:

instrukcje zapisuje się z wcięciem co powoduje, że wraz z wydłużeniem się

programu uwidacznia się struktura logiczna programu,

funkcję main () pozostawia się samą w wierszu,

nawiasy klamrowe pisze się w oddzielnych wierszach.

W zakresie stosowania wielkich i małych liter:

polecenia i nazwy funkcji pisze się małymi literami,

dużymi literami piszemy nazwy stałych i nazw symbolicznych.

background image

4. Podstawowe elementy programów w języku C

4.1. Jednostki leksykalne

Podana w rozdziale 3 definicja programu jako zestawu definicji i deklaracji

funkcji i zmiennych nie jest jedynym sposobem jego podziału na części.

Programy mogą być rozpatrywane również jako zestawy jednostek leksykalnych

i komentarzy oddzielonych odstępami: znakami spacji, tabulacji i przejścia do

nowego wiersza. Jednostkami leksykalnymi są: słowa kluczowe, identyfikatory,

literały, operatory i ograniczniki.

Słowami kluczowymi są napisy złożone z ustalonego ciągu małych liter

zastrzeżone w danym standardzie języka, i które mogą być używane tylko w

przypisanym im znaczeniu. Słowa kluczowe nie mogą być używane do

oznaczania obiektów programowych jak np. funkcje i zmienne. Jeżeli użyjemy

słowa kluczowego jako nazwy np. zmiennej, kompilator wygeneruje komunikat

o błędzie i zatrzyma proces kompilacji.

Poniżej przedstawiono słowa kluczowe standardu K&K języka C:

auto

goto

break

if

case

int

char

long

continue

register

default

return

do

short

double

sizeof

else

static

entry

struct

extern

switch

float

typedef

for

union

background image

Ze standardu ANSI C pochodzą dodatkowo słowa kluczowe:

const

enum

signed

void

volatile

W języku C++ dodano następujące słowa kluczowe:

catch

inline

cin

new

class

operator

cout

private

delete

protected

friend

Identyfikatory są to ciągi literowo – cyfrowe rozpoczynające się od litery, różne

od słów kluczowych. Ponieważ w sensie języka C literą jest także znak

podkreślenia „_” to pełny zestaw znaków alfanumerycznych dopuszczalnych w

identyfikatorach jest następujący:

litery A...Z oraz a...z,

cyfry 0...9 (cyfra nie może być pierwszym znakiem identyfikatora),

znak podkreślenia _

Należy zapamiętać, że niedopuszczalnymi znakami w identyfikatorach są

symbole : @ # $ ^ & ! ? oraz znaki operatorów arytmetycznych : + - / * %

Identyfikator może mieć dowolną długość, lecz tylko pierwsze 31 znaków ma

znaczenie dla kompilatora. Ze względu na czytelność programu dobrze jest

używać długich nazw dokładnie opisujących znaczenie identyfikatora. Praktyka

nakazuje też, aby nazwy różniły się przynajmniej jednym znakiem na początku

ciągu znaków.

Literałami nazywamy dane bezpośrednio wpisywane do instrukcji języka C.

Występują trzy rodzaje literałów: literały liczbowe (liczby), literały znakowe

background image

(znaki) i literały łańcuchowe (łańcuchy). Literał liczbowy składa się z ciągu cyfr

dziesiętnych i znaku kropki. Literał znakowy ma postać napisu „c” , w którym c

jest dowolnym znakiem różnym od znaków ‘ (apostrof) i \ (ukośnik). Literał

znakowy może być również złożony z opisem znaku.

Najczęściej używanymi opisami znaków są:

\n opis znaku nowego wiersza,

\r opis znaku tabulacji,

\r opis znaku powrotu karetki,

\t opis znaku tabulacji,

\’ opis znaku apostrofu,

\\ opis znaku ukośnika.

Literał łańcuchowy ma postać napisu ”s”, w którym s jest dowolnym ciągiem

znaków oraz opisem znaków. W odróżnieniu od innych języków

programowania, w języku C literał znakowy nie stanowi szczególnego

przypadku literału łańcuchowego.

Operatory są jednostkami leksykalnymi określającymi, w jaki sposób dane

(zmienne, stałe i wartości wyrażeń) mają być przetworzone. Operatory

arytmetyczne używane do wykonania działań matematycznych są to;

Operator

Funkcja

+

Dodawanie

- Odejmowanie

*

Mnożenie

/

Dzielenie

%

Reszta z dzielenia liczb całkowitych

Pozostałe operatory, bardzo istotne dla poprawnej składni instrukcji to przecinek

i średnik. Średnik jest stosowany w języku C do sygnalizowania końca

instrukcji, przecinek natomiast służy do rozdzielania elementów listy.

background image

Ogranicznikami w języku C są znaki */ zamykające komentarz oraz symbol \0

stanowiący specjalny kod jaki wstawia się po łańcuchu znaków

alfanumerycznych. Ogranicznik ten oznacza koniec łańcucha, dzięki czemu np.

funkcja puts() wie, gdzie przerwać pobieranie znaków do wyświetlania.

4.2. Stałe i zmienne

Jak wynika z samej nazwy, stała (constant) nigdy nie zmienia swojej wartości,

natomiast zmienna (variable) jest używana do reprezentowania różnych

wartości.

Wyrażenie to kombinacja stałych, zmiennych i operatorów stosowanych do

zapisu operacji arytmetycznych. W języku C instrukcja najczęściej to wyrażenie

zakończone średnikiem.

Przed użyciem w jakimś wyrażeniu zmiennej należy wcześniej ją zadeklarować.

Deklarowanie zmiennych oznacza dostarczenie kompilatorowi informacji o

nazwie i typie zmiennej. Deklaracja każdej zmiennej jest niezbędna z dwóch

powodów. Po pierwsze język C musi zarezerwować wystarczającą dużo pamięci

do przechowywania każdego elementu danej (różne typy danych zajmują różną

ilość pamięci). Po drugie, nie możliwe jest wykonanie wszystkich funkcji języka

C na wszystkich typach danych.

Deklaracje wszystkich użytych zmiennych w programie muszą być

zdefiniowane przed pierwszym ich użyciem, zwykle na początku bloku funkcji.

Definicja składa się z określenia typu oraz listy tego typu. Przykładowo, typ int

oznacza liczby całkowite, natomiast float – liczby rzeczywiste. Rodzaje typów

danych oraz zakresy wartości dla różnych typów omówione są w dalszej części

podręcznika.

Deklarowanie stałej polega na wskazaniu kompilatorowi języka C nazwy stałej i

jej wartości. Przed utworzeniem kody wynikowego, kompilator zastępuje

wystąpienie nazwy stałej jej wartością. W konsekwencji, sama nazwa stałej

nigdy nie będzie zamieniona w kod wynikowy.

background image

W większości programów korzysta się zarówno ze stałych, jak i zmiennych.

Pisząc program, jego autor musi zadecydować, jak będzie stosowany każdy

element informacji – jako stała czy jako zmienna. Korzystanie ze stałych ma

często na celu ułatwienie wprowadzania zmian do programu. Jeżeli jakieś dane

wykorzystywane są w programie wielokrotnie, a w całym programie posiadają

tą samą wartość to należy zastosować w tym przypadku stałe. W dalszym etapie

pisania i testowania programu ułatwi to modyfikowanie wartości tych danych,

ponieważ wystarczy dokonanie zmiany tej wartości tylko w jednym miejscu, a

nie w kilku czy kilkudziesięciu liniach tekstu źródłowego programu.

Wartość początkowa zmiennej zazwyczaj nie jest znana do chwili uruchomienia

programu. Przypisywanie wartości zmiennych może nastąpić albo w wyniku

przypisania jej wartości poprzez wpisanie jej z klawiatury, albo w wyniku

obliczeń. Można również nadać zmiennej jej wartość początkową poprzez

przypisanie jej w deklaracji albo w oddzielnej instrukcji. W przeciwieństwie do

stałej wartość ta może ulec zmianie w trakcie przebiegu programu.

Każdą stałą i zmienną w programie trzeba nazwać stosując ściśle określone

reguły sformułowane dla ogólnego pojęcia identyfikatora języka C (omówione

już w poprzednim rozdziale).

Maksymalna długość nazwy zmiennej i stałej zależna jest od stosowanego

kompilatora, zazwyczaj wynosi 32 lub 64 znaki. Nazwy zmiennych i stałych

mogą zawierać wielkie i małe litery oraz znaki podkreślenia (_). Przyjęto jednak

w języku C pewną konwencję, że nazwy zmiennych piszemy tylko małymi

literami, a nazwy stałych wielkimi. Można stosować dowolną kombinację liter i

liczb, ale wszystkie nazwy muszą zaczynać się od litery. Stosowanie znaku

podkreślenia ma zwykle na celu ułatwienie czytania nazwy identyfikatora i

uczynić ją bardziej znaczącą. Na przykład nazwa zmiennej kat_alfa jest

bardziej czytelna niż nazwa katalfa (w nazwach nie używamy znaku spacji).

Jako nazw zmiennych i stałych nie wolno stosować słów kluczowych języka C,

nazw funkcji bibliotecznych oraz poleceń (if ... else lub switch).

background image

4.3. Wyrażenia arytmetyczne

Wyrażeniem jest ciąg operatorów i operandów. Operatorem jest symbol

mówiący, jak przetworzyć operandy dając w wyniku jedną nową wartość. Np.

znak + oznacza dodanie dwóch operandów.

Operandem jest stała, zmienna lub wartość wyrażenia. Wyrażenie może więc

być operandem w innym wyrażeniu. W języku C operacja przypisania ma

wartość, zatem przypisanie też jest wyrażeniem.

Podobnie jak zmienne i stałe, również wyrażenia mają swój typ. Jest nim typ

wynikający z typów poszczególnych operandów niezbędnych do wykonania

poprawnych obliczeń.

4.3.1. Operatory arytmetyczne

W języku C wyróżnia się operatory jedno-, dwu- i trójargumentowe (unarne,

binarne i ternarne), co oznacza, że jedną wartość wynikową uzyskuje się na

podstawie jednej, dwóch lub trzech innych wartości.

Wymienione wcześniej operatory arytmetyczne znajdują się w grupie jedno- i

dwuargumentowych. Jednoargumentowym operatorem jest znak – (minus).

Operator ten można stosować do operandów typów całkowitych lub

rzeczywistych. Operator oznacza zmianę znaku liczby na przeciwny, np:

j = - j;

W standardzie języka C nie ma jednoargumentowego operatora + (plus).

Operatory matematyczne dwuargumentowe (+) (-) (*) realizują odpowiednio

dodawanie, odejmowanie i mnożenie operandów typu całkowitego lub

rzeczywistego. Należy zauważyć, że dla mnożenia operandów nie ma kontroli

poprawności wyniku, więc może on mieć wartość przypadkową, gdy poprawna

wartość nie mieści się w zakresie typu wyniku.

Operator / (ukośnik) wykonuje dzielenie pierwszego operandu przez drugi.

Operandy mogą mieć typ całkowity lub rzeczywisty. Jeżeli przynajmniej jeden

operand ma typ rzeczywisty, wynik jest typu rzeczywistego. Jeśli oba operandy

background image

są typu całkowitego, zaś wynik powinien mieć część ułamkową – jest ona

gubiona. Zaokrąglenie do liczby całkowitej dla wyniku dodatniego odbywa się

zawsze w dół. Jeśli wynik ma wartość ujemną, to sposób zaokrąglenia (w dół

lub w górę) zależy od implementacji. Wynik dzielenia przez zero jest

przypadkowy.

Operator % (procent) oblicza resztę z dzielenia pierwszego operandu przez

drugi, czyli realizuje funkcję modulo. Oba operandy muszą być typu

całkowitego. Jeśli oba mają wartość dodatnią, to wynik też jest dodatni. Jeśli

choć jeden operand ma wartość ujemną – znak wyniku zależy od implementacji.

Wynik obliczenia reszty z dzielenia przez jest niezdefiniowany.

4.3.2. Operatory przypisania

Podstawowa w języku C instrukcja przypisania wykorzystuje operatory

przypisania. Operator przypisania (=) w jednej operacji przetwarza prawy

operand i zapisują wynik do lewego operandu. Technicznie oznacza to, że

wartość prawego operandu zostaje zapisana w pamięci w miejscu, w którym

przechowywany jest operand lewostronny. Podobnie przypisanie:

a = b = 5

powoduje przypisanie wartości 5 zmiennej całkowitej a i następnie zmiennej

całkowitej b. Po zakończeniu wykonania tej instrukcji obie zmienne zawierają

wartość 5.

Operatory złożonego przypisania łączą w jednej operacji przypisanie i inną

operację arytmetyczną. Na przykład instrukcja:

a+=b;

jest rozumiana jako

a=a+b;

Poniżej zamieszczono listę złożonych operatorów przypisania:

background image

Operator

Opis

Operator = jest równoważny zapisowi

+=

dodawanie i przypisanie

x += y; x = x + y;

-=

odejmowanie i przypisanie

x -= y; x = x – y;

*=

mnożenie i przypisanie

x *= y; x = x * y;

/=

dzielenie i przypisanie

x /= y; x = x / y;

%=

reszta z dzielenia i przypisanie

x %=y;

x = x % y;

4.3.3. Operatory inkrementacji i dekrementacji

Operatory inkrementacji i dekrementacji są stosowane gdy wartość operandu

należy zwiększyć lub zmniejszyć o 1 (jeżeli operand jest liczbą). Operator

inkrementacji ++ zwiększa wartość operandu o 1, zaś operator inkrementacji

zmniejsza wartość operandu o 1.

Operatory inkrementacji i dekrementacji mogą występować przed albo za

operandem. Położenie operatora względem operandu wpływa na wartość

wyrażenia. Jeżeli operator występuje przed operandem, np.:

++x;

lub

–-x;

(operator pre-inkrementacji)

to wyrażenie ma wartość operandu po zwiększeniu lub zmniejszeniu. Jeśli

natomiast operator występuje za operandem, np.:

x++;

lub

x--;

(operator post-inkrementacji)

to wyrażenie ma wartość operandu przed jego zmianą.

4.4. Instrukcje

Jednym z najważniejszych elementów każdego języka programowania są

instrukcje. Umożliwiają one zapis algorytmu, a co za tym idzie - służą do

sterowania przebiegiem programu. W języku C/C++ ich lista jest dość krótka.

background image

Instrukcje te dzięki swej efektywności w połączeniu z różnorodnością typów

danych

i

operatorów

-

umożliwiają

tworzenie

nawet

najbardziej

zaawansowanego oprogramowania:

Instrukcje możemy podzielić na :

- instrukcje warunkowe,

- instrukcje cyklu,

- instrukcje sterujące,

- instrukcje złożone (grupujące),

- instrukcje puste.

4.4.1. Instrukcje warunkowe

W zakresie algorytmów, częstym problemem jest sprawdzenie, czy zachodzi

dana zależność i w zależności od tego wykonanie takich czy innych czynności.

Sprawdzenie warunków i przejście do wykonania odpowiednich operacji

umożliwiają tzw. instrukcje warunkowe. Do tej grupy zaliczamy instrukcję

warunkową if, oraz instrukcję wyboru switch.

Instrukcja warunkowa if ma postać:

if (wyrażenie) instrukcja1

else instrukcja2

lub

if (wyrażenie)

instrukcja1

Badana jest wartość wyrażenia. Jeśli wartość wyrażenia warunkowego jest różna

od zera (prawda), wówczas wykonywana jest instrukcja1, w przeciwnym razie

(fałsz) -instrukcja2.

Warunki w instrukcji if porównują wartości zmiennych lub stałych z literałem

lub inną zmienną lub stałą. Porównanie jest dokonywane z użyciem jednego z

operatorów relacji:

background image

Operator

Znaczenie

= =

równe

>

większe niż

<

mniejsze niż

> =

większe lub równe

< =

mniejsze lub równe

! =

nie jest równe

Stosowanie instrukcji if jest łatwe ale wymaga zapamiętania następujących

zasad:

- badane wyrażenie warunkowe musi znajdować się w nawiasach okrągłych,

- po warunku nie stawiamy średnika (pojawia się dopiero po całej instrukcji),

- w języku C nie ma instrukcji then rozdzielającej warunek i instrukcję do

wykonania (tak jest w innych językach programowania),

- umieszczenie wyrażenia warunkowego i instrukcji w oddzielnych wierszach

z wcięciem ułatwia czytanie programu.

Poniżej przedstawiono kilka przykładów stosowania instrukcji if:

if (wiek > 18) puts(” Jestes ju

ż

dorosly !”);

if (i > 0)

x+=10;

else {

x=0;

y=i;

}

Przykład zagnieżdżonej instrukcji if:

if (a==b) if (a==0) b=2; else a=2;

Instrukcje if mogą być zagnieżdżone dowolną liczbę razy. Przy użyciu instrukcji

if... else należy pamiętać, że słowo kluczowe else jest związane ze słowem

ostatnim if. W ostatnim przykładzie, wbrew intencji programisty, słowo

kluczowe else będzie związane nie z pierwszą instrukcją if, lecz z drugą.

background image

Przytoczona instrukcja warunkowa nie jest wykonywana tak jak instrukcja:

if (a==b)

{

if (a==0) b=2;

}

else

a=2;

Jest ona wykonywana tak jak instrukcja

if (a==b)

{

if (a==0)

b=2;

else

a=2;

}

W sytuacjach kiedy w danym fragmencie programu powinno się sprawdzać

występowanie jednocześnie dwu (lub więcej) warunków, można to wykonać

przy stosowaniu jednego z operatorów logicznych.

Operator && (dwa znaki ampersand) wykonuje operację iloczynu logicznego

(funkcja „i”, „and”) zaś operator || (dwie kreski pionowe) oznacza sumę

logiczną (funkcja „lub”, „or”). Operator && daje w wyniku wartość 1, gdy oba

operandy mają wartości różne od zera, w przeciwnym przypadku daje wartość 0.

Operator || daje w wyniku wartość 1, gdy przynajmniej jeden z operandów ma

wartość różną od zera, zaś wartość 0, gdy oba mają wartość 0.

Język C gwarantuje, że wyrażenie z operatorami logicznymi jest przetwarzane

kolejno od strony lewej do prawej. Jeśli wartość pierwszego operandu decyduje

już o wyniku operacji, to wartość drugiego operandu nie jest obliczana.

background image

Przykładem zastosowania operatora && jest testowanie wartości zmiennej.

Poniższa instrukcja warunkowa określa, czy zmienna y znajduje się w pewnym

zakresie wartości.

if (y>=0 && y<=100) puts(”OK”);

Operatorem logicznym jest także unarny operator negacji ! (wykrzyknik).

Generuje on wartość 0, gdy operand ma wartość różną od zera i wartość 1, gdy

operand ma wartość 0 (wartość 0 jest fałszywa). Przykładem zastosowania jest

fragment programu sprawdzający wartość zmiennej całkowitej zero.

if (!zero) puts(”Zmienna zero jest fałszywa”);

Powyższy warunek działa tak samo jak:

if (zero==)0

.

4.4.2. Instrukcja wyboru

Instrukcja wyboru switch (przełącz) umożliwia dokonanie wyboru spośród

nieograniczonej ilości możliwych instrukcji posługując się wartością wyrażenia

warunkowego i wariantami (case). Uogólniona składnia instrukcji switch jest

następująca:

switch (wyrażenie)

{

case wyrażenie1: instrukcja1;

case wyrażenie2: instrukcja;

...

default:

instrukcja-domyślna;

}

Instrukcja switch przekazuje sterowanie programu do instrukcji wewnątrz

bloku, zależnie od wartości wyrażenia warunkowego. Wykonanie instrukcji

wyboru rozpoczyna się od wyznaczenia wartości wyrażenia sterującego

(występującego w nawiasach po słowie kluczowym switch). Wartość ta musi

być wyrażona jako liczba całkowita, literał znakowy w apostrofach lub jako

background image

nazwa stałej całkowitej lub znakowej. Wyznaczona wartość wyrażenia

sterującego jest następnie porównywana kolejno z wartościami wyrażeń stałych

występujących w przedrostkach należących do danej instrukcji decyzyjnej

(nazywanych etykietami wariantówcase label). Przy definiowaniu wyrażeń

stałowartościowych używanych w instrukcji switch-case należy zwrócić uwagę,

aby zostały one tak dobrane, by były wzajemnie wykluczające się. W razie

„nakładania się” wariantów wykonany będzie pierwszy spełniający warunek

zgodności wyrażenia sterującego i wartości przedrostka case. (licząc od góry).

Po stwierdzeniu równości, wykonywana jest instrukcja poprzedzona danym

przedrostkiem case oraz wszystkie instrukcje po niej następujące, aż do

wykonania instrukcji break albo do wykonania ostatniej instrukcji wnętrza

instrukcji decyzyjnej. Jeśli równość nie zostanie stwierdzona, to wykonywana

jest instrukcja poprzedzona przedrostkiem default: i ewentualne instrukcje

występujące po niej. Jeśli nie stwierdzono równości i nie użyto przedrostka

default:, to wykonanie instrukcji decyzyjnej jest uznawane za zakończone.

Poniższy przykład ilustruje opisane powyżej cechy instrukcji switch:

switch (i)

{

case ‘1’:

case ‘2’:

puts(”i=1 lub i=2”);

case ‘3’:

puts(”i!=1”);

puts(”i!=2”);

puts(”i=3”);

break;

default:

puts(”i ma warto

ść

inna ni

ż

cyfry 1-3”);

}

background image

4.4.3. Instrukcje cyklu

Instrukcje cyklu umożliwiają wielokrotne wykonywanie pewnych sekwencji

instrukcji czyli pętli, które jak wiadomo, są podstawą programowania. Pętle

programowe są nazywane także iteracją a służą do powtarzania tych samych

instrukcji wielokrotnie, aż do chwili, gdy zostaną spełnione określone warunki.

W języku C wprowadzono trzy instrukcje iteracyjne:

- instrukcję for (”dla”),

- instrukcję while (”dopóki”),

- instrukcję dowhile (”wykonuj - dopóki” )

4.4.3.1. Instrukcja for

Instrukcja cyklu for ma postać:

For (wyrażenie_inicjujące; wyrażenie_warunkowe; wyrażenie_zwiększające)

instrukcja

Wyrażenie_inicjujące, wyrażenie_warunkowe i wyrażenie_zwiększające

opcjonalne, to znaczy mogą być pominięte.

Wykonywanie pętli odbywa się w następujący sposób:

1. Obliczane jest wyrażenie_inicjujące, co najczęściej powoduje zainicjowanie

liczników pętli,

2. Obliczane jest wyrażenie_warunkowe, jeśli jest ono niezerowe (prawda) to

wykonywana jest instrukcja związana z instrukcją for,

3. Obliczane jest wyrażenie_zwiększające, co powoduje zwykle zwiększenie lub

zmniejszenie liczników pętli,

4. Ponownie obliczane jest wyrażenie_warunkowe, jeśli jego wartość jest różna

od zera - wykonywane są ponownie czynności z punktu 2.

Puste wyrażenie warunkowe ma wartość logiczną prawda, tzn. instrukcja:

background image

For (;;)

{

... tu instrukcja

}

jest równoważna instrukcji,

For (;1;)

{

... tu instrukcja

}

Przykładem wykorzystania instrukcji for może być fragment programu, w

którym obliczana jest wartość iloczynu odpowiedniej liczby tych samych

czynników ”

x

” (odpowiada to obliczeniu naturalnej n-tej potęgi liczby x).

Występuje tylko jedna zmienna liczników : ”

i”

- typu całkowitego.

il=1;

For (i=0, i<n, ++i)

i1*=x;

4.4.3.2. Instrukcja while

Ogólna postać instrukcji pętli while ma postać:

while (wyrażenie_warunkowe) instrukcja

Badane jest wyrażenie_warunkowe i jeśli ma ono wartość różną od zera, to

wykonywana jest instrukcja, po czym znów badane jest wyrażenie_warunkowe.

Jeśli wyrażenie_warunkowe ma wartość zero – wykonywanie pętli kończy się.

Ciało

pętli

jest

wykonywane

zero

lub

więcej

razy,

dopóki

wyrażenie_warunkowe nie będzie miało wartości 0.

Przykład zastosowania:

i=5;

while (i>0) –-i;

background image

4.4.3.3. Instrukcja do – while

Instrukcja cyklu do ma postać:

do

instrukcja

while (wyrażenie_warunkowe)

Instrukcja jest wykonywana do momentu, gdy wyrażenie_warunkowe osiągnie

wartość zero (fałsz). Instrukcję do - while można przetłumaczyć jako :wykonuj

instrukcję, dopóki wartość wyrażenia_warunkowego oznacza prawdę. Instrukcję

tą stosujemy zamiast instrukcji while wówczas, gdy zależy nam, aby wykonana

została przynajmniej jedna instrukcja.

Przykład:

a=b=0;

do {

a+=b;

b++;

}

while (b<10);

4.4.4. Instrukcje sterujące

Instrukcje te umożliwiają opuszczanie pętli, przeniesienie wykonywania

programu o kilku poziomów pętli niżej, zakończenie wykonywania i zwrócenie

wartości funkcji. W języku C można wyróżnić następujące instrukcje sterujące:

- instrukcja break (”przerywaj”),

- instrukcja continue (”kontynuuj”),

- instrukcja return (”zwróć wartość i powróć”),

- instrukcja goto (”skoku”).

background image

4.4.4.1. Instrukcja break

Instrukcja „zaniechania” break może być użyta tylko w instrukcjach cyklu (for,

while, do...while) oraz w instrukcji wyboru (swith). Powoduje ona opuszczenie

aktualnego poziomu pętli lub instrukcji wyboru. Należy pamiętać, że instrukcje

iteracyjne oraz instrukcje wyboru mogą być wielokrotnie zagnieżdżone, przed

użyciem instrukcji break należy zastanowić się, czy opuszczany jest właściwy

poziom. W celu przeniesienia wykonywania programu o kilka poziomów pętli

niżej należy użyć instrukcji skoku (goto).

4.4.4.2. Instrukcja continue

Instrukcja continue może być używana tylko wewnątrz instrukcji iteracyjnych.

Przerywa ona wykonywanie bieżącej iteracji pętli i przenosi sterowanie do

następnej iteracji (do następnego kroku wykonywania pęteli).

Jej użycie powoduje:

- w przypadku pętli while i do - while - przeniesienie sterowania z wnętrza

pętli do badania wyrażenia warunkowego.

- w przypadku pętli for - przeniesienie sterowania do wyrażenia

zwiększającego liczniki pętli, a następnie badane jest wyrażenie warunkowe.

- w przypadku wielokrotne zagnieżdżonych pętli instrukcja ”kontynuuj” jest

wiązana z najbliższą instrukcją iteracyjną.

4.4.4.3. Instrukcja return

Instrukcja return (”zwróć wartość i powróć”) ma postać:

return wyrażenie;

Napotkanie tej instrukcji powoduje zakończenie wykonywania funkcji w której

występuje i przeniesienie sterowania do funkcji wywołującej. Jeśli zwraca ona

wartość, wykonanie omawianej instrukcji spowoduje zwrócenie wartości

funkcji.

background image

4.4.4.4. Instrukcja goto

W języku C istnieje instrukcja skoku umożliwiająca przekazanie sterowania do

określonego miejsca wewnątrz aktualnie wykonywanej funkcji programu.

Instrukcja skoku ma postać:

goto etykieta;

Etykieta określa miejsce w programie, do którego ma nastąpić skok; należy przy

tym pamiętać, że niemożliwe są skoki do miejsc znajdujących się poza funkcją,

do której należy instrukcja skoku. Etykieta składa się z identyfikatora (nazwy),

bezpośrednio po którym powinien znajdować się znak dwukropka (:),

Przykład użycia:

if (jestblad)

goto koniec;

...

/*tu normalne instrukcje*/

koniec:

return jestblad;

Należy pamiętać, że instrukcji goto nie należy nadużywać, gdyż jej stosowanie

zmniejsza czytelność programu i jest uzasadnione tylko w wyjątkowych

okolicznościach.

4.4.5. Instrukcja grupująca

Instrukcja grupująca (złożona), to deklaracje i instrukcje w nawiasach

klamrowych:

{

deklaracja

....

instrukcja

....

}

background image

Zwykle instrukcja złożona występuje jako ciało innej instrukcji, w miejscu,

gdzie powinna być jedna instrukcja. Deklaracje można pominąć, natomiast w

nawiasach musi wystąpić przynajmniej jedna instrukcja,

4.4.6. Instrukcja pusta

W języku C wyróżnia się tzw. instrukcje pustą, tj. taką, która nie powoduje

wykonania żadnych czynności. Instrukcję taką zapisuje się w postaci średnika

nie poprzedzonego żadnym słowem kluczowym, ani wyrażeniem.

Na przykład:

while (...)

;

/* to jest instrukcja pusta */

Jak widać, instrukcja pusta jest zwykle związana z instrukcją iteracyjną.

4.5. Deklaracje

Dane podlegające przetwarzaniu są reprezentowane przez nazwy. Najprostszymi

nazwami danych są literały i identyfikatory. Pierwsze nich reprezentują w

programach stałe, a drugie – zmienne. Pojęcia stałych i zmiennych zostały już

wyjaśnione w rozdziale 4.2. podręcznika, należy jednak wyjaśnić teraz zasady

ich deklarowania.

4.5.1. Deklarowanie zmiennych prostych

Zmienną prostą jest zmienna, której mogą być przypisywane jedynie dane

skalarne. W języku C dane te należą do jednego z następujących typów:

char

(znakowy)

int

(całkowity)

short

(całkowity krótki)

long

(całkowity długi)

unsigned

(całkowity długi)

float

(zmiennopozycjny krótki)

double

(zmiennopozycyjny długi)

background image

4.5.1.1. Zmienne znakowe

Zmienną znakową (character variable) deklaruje się poprzez użycie słowa

kluczowego char i nadanie zmiennej jakiejś wybranej nazwy (identyfikatora

zmiennej), np.:

char nazwa_zmiennej;

char x,y,z;

Drugi przykład dotyczy tzw. grupowej deklaracji zmiennych.

Język C pozwala na zainicjowanie zmiennej (czyli przypisanie jej początkowej

wartości) jednocześnie z jej zadeklarowaniem. Poniższa deklaracja jednocześnie

przypisuje zmiennej

znak

wartości – kod dużej litery A:

char znak = ‘A’;

Należy zauważyć, że dana typu char może być także pojedyńczą cyfrą, np.:

char z = ‘7’;

Jednak w języku C istnieje rozróżnienie między znakiem ”7” a cyfrą 7. Zmienna

znakowa z powyższego przykładu będzie miała wartość kodu ASCII znaku

cyfry ‘7’, czyli liczby dziesiętnej 55.

4.5.1.2. Zmienne typu całkowitego

Liczba całkowita int (integer)– nie ma miejsc dziesiętnych. Może być to liczba

dodatnia lub ujemna, a także zero, ale nie może mieć żadnych miejsc po kropce

dziesiętnej. Dlatego wynik dzielenia w zbiorze liczb całkowitych podlega

obcięciu (truncate) do części całkowitej. W zależności od stosowanego

kompilatora języka C długość liczb całkowitych deklarowanych jako typ C

może być różna. Kompilator Borland C++ 3 przystosowany do 16 bitowego

ś

rodowiska MS DOS posługuje się 16-bitowymi liczbami typu int. Oznacza to,

ż

e zmienne zadeklarowane w jednym z wymienionych typów całkowitych mogą

przyjmować wartości:

background image

short int

Liczba całkowita między 0 a 255

int

Liczba całkowita między –32 767 a 32 768

long int

Liczba całkowita między –2 147 483 648

a 2 147 483 648

unsigned long

Dodatnia liczba całkowita między 0 a 4 294 967 295

Wersje kompilatorów dla 32-bitowego środowiska Windows 95/NT np.

VISUAL C++ posługują się już liczbami typu int z zakresu:

od –2 147 483 648 do 2 147 483 647.

Format deklaracji zmiennej całkowitej jest następujący:

int nazwa_zmiennej;

Przykłady deklaracji zmiennych typu całkowitego:

short int s=255;

/* deklaracja zmiennej z

przypisaniem wartosci */

int liczba1,liczba2; /*grupowa deklaracja zmiennych*/

unsigned int r; /* liczba całkowita bez znaku */

long k;

/ * długa liczba całkowita ze znakiem */

long unsigned int m; /* długa liczba całkowita bez

znaku */

4.5.1.3. Zmienne typu zmiennopozycyjnego

Liczby zmiennoprzecinkowe float (skrót od: floating point number) w

przeciwieństwie do liczb całkowitych zawierają przecinek dziesiętny. Liczby te

są inaczej nazywane liczbami rzeczywistymi (real number).

Ponieważ liczby zmiennopozycyjne mogą być bardzo małe lub bardzo duże,

więc zakres ich wartości często wyraża się jako liczby wykładnicze (scientific

notation). W takim formacie liczba przedstawiona jest w postaci mantysy i

wykładnika. Cecha i mantysa są rozdzielone literą E lub e.

[mantysa] e [wykładnik] lub [mantysa] E [wykładnik]

background image

Podobnie jak w przypadku liczb całkowitych, liczby zmiennoprzecinkowe typu

float posiadają ograniczony zakres dopuszczalnych wartości. Standard ANSI C

wymaga, aby zakres ten wynosił o najmniej +/- 1.0*10

37

. Normalnie do

reprezentowania liczb typu float używane są 32 bity (4 bajty). Wynika z tego, że

liczba zmiennoprzecinkowa w C może mieć co najmniej sześć cyfr znaczących

dokładnych. Dodatkowo, w języku C można zadeklarować zmienną typu

double, do której przechowywania wykorzystywane jest dwukrotnie więcej

bitów, niż do przechowywania liczby typu float. Dlatego liczba

zmiennopozycyjna typu double to liczba, o podwójnej precyzji, posiadająca co

najmniej 10 cyfr znaczących dokładnych. Aby komputer prawidłowo

rozpoznawał format liczb, liczby zmiennoprzecinkowe mogą być wyposażone w

przyrostek (suffix) f lub F na końcu, np.: 7.01f, -3.14F.

Liczba rzeczywista bez takiego przyrostka jest domyślnie traktowana jako liczba

typu double.

Format deklaracji zmiennej numerycznej, zmiennoprzecinkowej:

float nazwa_zmiennej;

Przykłady deklaracji zmiennych rzeczywistych:

float x;

/*liczba pojedynczej precyzji */

DOUBLE z;

/*liczba podwójnej precyzji */

float a=3.14; /*deklaracja z przypisaniem wartosci*/

4.5.2. Deklarowanie zmiennych typu wskaźnikowego

Wartością zmiennej wskaźnikowej jest wskaźnik czyli adres obszaru pamięci

zajmowanego przez jakiś obiekt. Przez wskaźnik można w sposób pośredni

odwołać się do tego obiektu. Charakterystyczne przykłady zastosowania

wskaźników wiążą się z tworzeniem list, oraz z zarządzaniem obiektu, dla

których pamięć jest przydzielana dynamicznie podczas wykonywania programu.

Zrozumienie istoty wskaźników i opanowanie technik ich stosowania ma istotne

znaczenie dla skutecznego programowania w języku C. Dotychczas

background image

odwoływaliśmy się do zmiennych bezpośrednio, to znaczy używając ich nazw –

identyfikatorów. Rozpatrując zmienne typu wskaźnikowego należy wprowadzić

pojęcie pośredniego odwoływania się do obiektów (wskaźnikiem można

wskazywać nie tylko zwykłe zmienne, ale także np. funkcje). Takie pośrednie

odwoływanie się do obiektów ma nazwę indirection.

Zamiast bezpośredniego przyporządkowania zmiennej jakiejś wartości, można

zmiennymi i ich zawartością manipulować pośrednio, posługując się

wskazywaniem określonych adresów w pamięci RAM, bez używania

identyfikatorów, które tym „komórkom pamięci” zostały uprzednio nadane.

Utworzenie nowej, specjalnej zmiennej zwanej wskaźnikiem (pointer variable)

powoduje, iż możemy w pamięci RAM przechowywać adres innej zmiennej.

Znajomość adresu danych w pamięci i odwołanie się do nich poprzez wskazanie

adresu – to często najszybsza i najbardziej efektywna metoda sięgania do tych

danych.

Podsumowując powyższe rozważania można podać następującą definicję

wskaźnika:

Wskaźnik (pointer) to zmienna używana do wskazywania innej zmiennej.

Zgodnie z powyższą definicją wskaźnik można określić również jako zmienna

adresowa (pointer – adress variable).

W chwili deklarowania dowolnej zmiennej, pewien fragment wolnej do tej pory

pamięci musi zostać „zarezerwowany” dla tej zmiennej, a do nazwy –

identyfikatora zmiennej zostaje przyporządkowany adres miejsca w pamięci.

Adres przyporządkowany zmiennej jest nazywany (wartością lewostronną –

variable left value). Przykładowo, po zadeklarowaniu i zainicjowaniu zmiennej:

int x;

x=7;

zmienna x ma dwie wartości:

wartość lewostronna: 1000

wartość prawostronna: 7

background image

W tym przypadku wartość lewostronna to adres (numer bajtu) w pamięci, gdzie

przechowywana jest zawartość zmiennej, jej wartość prawostronna – 7.

Posługując się lewostronną wartością zmiennych, kompilator C może łatwo

zlokalizować odpowiednie miejsce w pamięci zarezerwowane dla danej

zmiennej, a następnie posługiwać się tym adresem do poprawnego odczytu i

zapisu prawostronnej wartości zmiennej.

Język C zawiera specjalny operator & zwracający wartość lewostronną

zmiennej, nazywany operatorem adresowym (adress-of-operator). Dla

przykładu fragment programu:

long int x;

y=&x;

powoduje przypisanie zmiennej y adresu w pamięci zmiennej całkowitej x.

Działanie odwrotne powoduje operator deferencji

*

. Zapis:

long int a;

a=

*

y;

oznacza:

- pobierz zawartość spod adresu pamięci wskazywanego przez zmienną

(wskaźnik) y i przyporządkuj ją, jako nową wartość zmiennej a.

Wskaźnik jako zmienna także ma swoją wartość lewostronną i wartość

prawostronną, jednakże w przypadku wskaźnika obie te wartości oznaczają

adresy w pamięci. Wartość lewostronna wskaźnika – jego własny adres – służy

do odwoływania się do samego wskaźnika. Wartość prawostronna wskaźnika –

zawartość wskaźnika ro adres jakiejś innej zmiennej w pamięci.

Uogólniony format deklaracji wskaźnika jest następujący:

typ_danych *nazwa_wskaźnika;

Symbol „gwiazdki” (

*

= ‘asterisk’) wskazuje, że zmienna jest wskaźnikiem.

Poniżej zamieszczono przykłady kilku deklaracji wskaźników, wskazujących

adresy danych różnych typów:

background image

char ptr_c;

/*wskaznik do zmiennej typu char*/

int

*

ptr_int;

/*wskaznik do zmiennej typu int*/

float

*

ptr_c;

/*wskaznik do zmiennej typu float*/

4.5.3. Deklarowanie zmiennych typu tablicowego

Tablica jest zestawieniem obiektów tego samego typu (set of variables). W tym

przypadku poszczególne obiekty nie mają nazw, a dostęp do nich jest

możliwy tylko przez podanie położenia danego obiektu w tablicy. Ten rodzaj

dostępu do obiektu nazywa się indeksowaniem.

Uogólniony format deklaracji jest następujący:

typ_danych nazwa_tablicy[rozmiar_tablicy];

gdzie typ_danych to typ wszystkich zmiennych wchodzących w skład tablicy,

nazwa_tablicy to identyfikator nadawany zgodnie z regułami C, rozmiar_tablicy

określa, ile elementów zawiera tablica. Całkowitą ilość bajtów pamięci

zajmowanych przez daną tablicę można obliczyć posługując się wzorem:

sizeof(typ_danych)*rozmiar_tablicy

gdzie sizeof jest operatorem rozmiaru.

W języku C nie ma ograniczeń liczby wymiarów tablicy, pod warunkiem, że

całkowity rozmiar tablicy nie przekracza 64kB.

Przykłady deklaracji zmiennych tablicowych:

CHAR str[30] /* tablica 30 znaków */

CHAR *t[12] /* tablica 12 wska

ź

ników znaków */

DOUBLE tabdb1[30] /* tablica 30 liczb DOUBLE */

DOUBLE *tabdb1p[10] /* tablica 10 wska

ź

ników liczb

typu DOUBLE */

W języku C można również deklarować tablice o większej ilości wymiarów

(macierze wielowymiarowe). Uogólniony format deklaracji tablicy n -

wymiarowej jest następujący:

typ_danych nazwa_tablicy[wymiar_1] [wymiar_2] ... [wymiar_n]

background image

gdzie n jest dodatnie i całkowite.

Przykład deklaracji:

INT tab[10][20] /*dwuwymiarowa tablica liczb

całkowitych */

Deklarację tablicy można połączyć z jej inicjacją. Polega ona na wymienianiu

bezpośrednio po deklaracji zmiennej listy inicjatorów tablicy ujętych w nawiasy

{}. Przy inicjowaniu tablic obowiązują zasady:

- lista inicjatorów nie może zawierać więcej elementów niż jest wstanie

pomieścić tablica,

- w języku C inicjator musi być stałą,

- jeśli inicjatorów jest mniej niż elementów tablicy, wówczas pozostałe

elementy tablicy są inicjowane zerami w przypadku tablicy liczb lub znaków

oraz wskazaniami pustymi NULL w przypadku tablicy wskaźników.

Przykłady deklaracji tablic z inicjowaniem wartości:

LONG DOUBLE tabdb1[30]={10.01,20,30};

DOUBLE tab1[2][3]={{3,1},{-10,90},{2,-2}};

W tablicy tabdb1 zainicjowano pierwsze trzy elementy, pozostałym zostanie

domyślnie przypisana wartość zero. Deklaracja tablicy tab1 pokazuje możliwość

użycia nawiasów klamrowych dla wygodniejszej orientacji w strukturze

inicjowanej tablicy.

W języku C istnieje również możliwość zadeklarowania tablicy o

nieokreślonych rozmiarach. W pierwszym specyfikatorze rozmiaru tablicy nie

podaje się wówczas wymiaru, nawiasy klamrowe pozostawiamy puste.

Na przykład:

INT n[][s][10];

CHAR *txt[];

background image

4.5.4. Deklaracje łańcuchów znakowych

Łańcuch (napis) w języku C jest tablicą znaków zakończoną znakiem o kodzie

zero: /0 (ogranicznikiem łańcucha). Łańcuchy deklarujemy tak samo, jak tablice

znaków np.:

CHAR str[100]

/* ła

ń

cuch o rozmiarze 100 znaków */

Biorąc pod uwagę fakt, że ostatni element jest zarezerwowany dla ogranicznika

łańcucha, trzeba zawsze deklarować tablicę o jeden element większą niż wynosi

maksymalna liczba znaków.

Deklarację łańcucha można połączyć z jego inicjacją:

CHAR[8]=”/n/f(ADAS)”;

CHAR str [8]={‘/n’,’/f’,’(’,’A’,’D’,’A’,’S’,’/0’};

CHAR *str1=”ADAS”;

Inicjowanie łańcuchów znakowych może odbywać się przez podanie jego

zawartości w cudzysłowach (tak jak w pierwszej deklaracji) lub przez

wymienienie kolejno stałych znakowych określających zawartość łańcucha (tak

jak w drugiej deklaracji). Jest jednak znaczna różnica, między tymi sposobami

inicjacji, gdyż w pierwszym przypadku kompilator sam uzupełnia deklarację o

znak ‘/0’ oznaczający koniec łańcucha znaków, w drugim zaś musi zrobić to

programista. Trzeci z powyższych deklaracji pokazuje, w jaki sposób można

deklarować łańcuchy z wykorzystaniem wskaźników. W takim przypadku

kompilator przydziela pamięć łańcuchowi wymienionemu w części inicjacyjnej

deklaracji, a zmiennej wskaźnikowej przypisuje adres jego początku.

4.5.5. Deklarowanie struktur

Tablice (macierze) służyły do zgrupowania wielu zmiennych tego samego typu,

natomiast w języku C można łączyć zmienne różnych typów w tzw. struktury.

Struktura to połączenie w jedną całość danych różnego typu w taki sposób, że

mogą one być traktowane jako jeden moduł (unit). Pomiędzy tablicami i

strukturami jest wiele różnic. Oprócz faktu, że w obrębie jednej struktury

background image

znajdują się dane różnych typów, każdy element danych w strukturze ma swoją

nazwę zamiast numeru indeksu, jak to było w tablicach.

Deklaracje zmiennych typu strukturalnego mogą mieć dwie formy. W pierwszej

deklaruje się zmienne równocześnie ze składnikami struktury:

struct {

deklaracja_składowych

} identyfikator_struktury1,identyfikator_struktury2,...

Wyróżnikiem deklaracji struktury jest słowo kluczowe struct. Po nim w

nawiasach klamrowych wymienione są poszczególne składniki. Lista

składników jest podobna do deklaracji zmiennych, gdyż zawiera ich typy i

identyfikatory (bez wyrażenia inicjującego). Deklarację kończy lista zmiennych

typu strukturalnego.

Przykład:

Struct {

char nazwisko[15];

char imie[19];

unsigned short wiek;

it telefon, pensja;

} osoba;

Deklarację można uzupełnić o nazwę typu strukturalnego. Umieszcza się ją po

słowie kluczowym struct. W deklaracji z nazwą typu strukturalnego można

pominąć nazwy zmiennych i wówczas jest ona deklaracją typu.

struct typ_structur {

typ1 zmienna1;

typ2 zmienna2;

typ3 zmienna3;

.......

};

background image

Identyfikator typ_structur może być wybrany dowolnie, ale z uwzględnieniem

reguł nazewnictwa w języku C. Po nim w nawiasach klamrowych wymienione

są poszczególne składniki tj. pola struktury. Deklaracja musi być zamknięta

nawiasem klamrowym i średnikiem.

Jeżeli w programie zadeklarowany jest typ strukturalny, czyli struktura ma

nadaną nazwę, można użyć deklaracji zmiennych wykorzystującej tę nazwę, np.:

Struct typ_osoba ojciec,matka;

Deklaracja w tej formie używa zdefiniowanego wcześniej typu strukturalnego

jako typu definiowanych zmiennych.

Inicjacja struktury polega na umieszczeniu w nawiasach klamrowych

inicjatorów poszczególnych pól zgodnie z kolejnością ich występowania w

definicji typu struktury. Liczba inicjatorów nie może przekraczać liczby

składowych struktury, jeśli zaś jest mniejsza od tej liczby, wówczas pozostałe

pola są inicjowane zerami (pola rzeczywiste i całkowite) lub adresami pustymi

(pola wskaźnikowe).

Struktura jest inicjowana przy pomocy listy danych, nazywanych listą

inicjującą. Poszczególne pozycje danych są na takiej liście rozdzielane

przecinkami.

Przykład inicjowania struktury:

struct liczba_urojona {

double x,c;

}

struct liczba_urojona a={3.14,2.75}

4.5.6. Deklarowanie unii

Unia jest to blok pamięci do przechowywania różnych elementów. Deklaracje

unii są identyczne jak deklaracje struktur, a wyróżnia je słowo kluczowe union,

np.:

background image

union {

char c;

int i;

long l;

} przykład;

Unię od struktury odróżnia to, że w każdej chwili może ona zawierać jedną

warstwę spośród wymienionych w deklaracji jako składniki. Unia ma

zarezerwowane w pamięci miejsce na swój największy składnik, a nie na

wszystkie. Stosowanie unii pozwala na wykorzystanie tego samego miejsca w

pamięci do różnych celów. Ilustruje to następujący przykład:

struct znajomi {

char nazwisko[15], imie[10];

long data_urodzenia;

int telefon;

char plec; /* ‘m’==on, ‘k’==ona */

union {

char nazw_rod[15];

int stopien;

} xx;

} przyjaciele;

W przykładzie tym składnikiem struktury jest unia o nazwie xx zawierająca

bądź tablice znakową nazw_rod, w której jest zapisane nazwisko panieńskie,

bądź liczbę całkowitą stopien, będącą indeksem w tablicy stopni wojskowych.

Rozpoznawanie, która wartość jest zapisana w unii następuje po sprawdzeniu

wartości składnika plec określającego płeć osoby.

background image

4.5.7. Deklarowanie stałych

Zadeklarowanie stałej oznacza wskazanie kompilatorowi nazwy stałej i jej

wartości. Wykonuje się to przed funkcją main() za pomocą dyrektywy

kompilatora #define mającej ogólną składnię:

#define nazwa wartość

W języku C rozróżniane są cztery rodzaje stałych: całkowite, rzeczywiste, znaki

i łańcuchy znaków.

Stała całkowita może być zapisana jako liczba dziesiętna, oktalna lub

heksydecymalna. Stała dziesiętna, to liczba złożona z cyfr od 0 do 9. Stała

oktalna (ósemkowa) rozpoczyna się zawsze od cyfry 0, po której następują cyfry

oktalne (od 0 do 7). Stałą heksadecymalną (szesnastkową) rozpoczyna cyfra 0,

po której następuje litera x lub X i dalej cyfry heksadecymalne (cyfry od 0 do 9 i

duże lub małe litery od a do f).

Stałe całkowite dziesiętne mają typ int lub long, zależnie od wartości stałej.

Jeżeli stała mieści się w zakresie liczb typu int – otrzymuje ten typ, a jeśli nie –

przyjmuje typ long.

Stałe rzeczywiste składają się z:

- dziesiętnej części całkowitej,

- kropki oddzielającej część całą od ułamkowej,

- dziesiętnej części ułamkowej,

- litery E lub e, po której występuje całkowity wykładnik potęgi,

- przyrostów F, f, L, l (opcjonalnie)

Standardowo stałe rzeczywiste są przechowywane w postaci liczb typu

DOUBLE, a nadanie zmiennej należącej do jednego z pozostałych typów

rzeczywistych wymaga wykonania konwersji z typu DOUBLE. Aby tego

uniknąć należy na końcu stałej rzeczywistej dodać przyrostek F lub f dla typu

FLOAT zaś dla liczb LONG DOUBLE przyrostek L lub l.

background image

W przypadku stałych znakowych przypisywana wartość musi znaleźć się w

apostrofach. Podobnie w celu deklaracji łańcucha należy wartość ująć w

cudzysłowy.

Przykłady deklaracji stałych:

#define liczba_calkowita 3

#define liczba_rzeczywista 3.14

#define znak ‘A’

#define lancuch ”ABC”

Kompilator języka C++ pozwala na deklarowanie stałych wewnątrz funkcji, a

nie jak dyrektywę #define przed main(). Przy pomocy modyfikatora const

deklaruje się stałą, określa jej typ i przypisuje wartość.

Przykłady deklaracji stałych w języku C++:

const int liczba=8;

const char znak=’a’;

4.6. Funkcje

Funkcja jest zestawem deklaracji i instrukcji, zwykle utworzonym do wykonania

jakiegoś zadania. Deklaracje i instrukcje jednej funkcji są niezależne od

deklaracji i instrukcji w innej funkcji. W dużych programach celowe jest

podzielenie całości na mniejsze zadania, co upraszcza tworzenie i testowanie

programu, a także czyni go czytelniejszym. Inna zaleta stosowania funkcji to

oszczędność czasu i pamięci. Raz napisana i skompilowana funkcja może być

używana wielokrotnie w różnych programach. Funkcja jest zwykle umieszczana

w jednym miejscu pamięci, do którego następują odwołania w momencie, gdy

wywoływana jest funkcja. Kolejne wywołania funkcji zwiększają długość kodu

wynikowego programu nieznacznie w porównaniu z długością funkcji.




background image

4.6.1. Deklaracja funkcji

Deklaracja w odróżnieniu od definicji, jest pojęciem logicznym, gdyż stanowi

tylko informację dla kompilatora, że funkcja o określonej nazwie, typie wartości

i parametrów może zostać użyta gdzieś w danym module programu. Definicja

funkcji określa natomiast, co funkcja właściwie robi. Stanowi więc ona zapis

jakiegoś algorytmu lub jego części. Definicję funkcji powoduje, w odróżnieniu

od deklaracji, przydzielenie obszaru pamięci, w którym znajduje się kod

wynikowy funkcji.

W standardzie ANSII oraz w C++ deklaracja funkcji nazywana jest prototypem i

wygląda tak:

specyfikator_typu_danych identyfikator_funkcji (

specyfikator_typu_danych1 argument1,

specyfikator_typu_danych2 argument2,

...

specyfikator_typu_danych_n argument_n

);

W deklaracji powyższej specyfikator_typu_danych określa typ wartości funkcji.

Jeśli nie został on określony w sposób jawny, przyjmowany jest typ INT,

natomiast lista deklaracji parametrów składa się z specyfikatorów typów

parametrów.

Przykłady prototypów funkcji:

void funkcja (void); /*bezparametrowa funkcja nie

zwracajaca wartosci */

fun (int); /* funkcja o wartosci typu INT(domyslnie)

i jednym

parametrze calkowitym (INT) */

void fun2 (int); /* funkcja o jednym parametrze

calkowitym, nie zwracajaca wartosci */

DOUBLE max(double l1, l2);

/* funkcja o parametrach

i wartosci typu rzeczywistego (DOUBLE) */

background image

W języku C/C++ nie rozdziela się pojęcia funkcji i procedury - odpowiednikiem

procedur są funkcje o wartościach typu void, czyli nie zwracające wartości.

Zaleca się sygnowanie braku parametrów słowem kluczowym void, np.:

int func(void);

4.6.2. Definicja funkcji

Definicja stanowi o tym, co dana funkcja wykonuje. Definicja składa się zwykle

z 5 występujących kolejno po sobie elementów:

1. opcjonalnych specyfikatorów extern, static, inline (C++),

2. typu wartości funkcji,

3. identyfikatora (nazwy) funkcji,

4. listy deklaracji parametrów funkcji, która może być pusta, co oznacza

funkcję bezparametrową (dla oznaczania braku parametrów, lepiej używać

słowo kluczowe void),

5. ciała funkcji umieszczonego w nawiasach klamrowych.

Ad. 1.

Specyfikator extern informuje, że funkcja ma być dostępna również poza

modułem programu, w którym znajduje się jej definicja. Użycie static

powoduje, że funkcja będzie dostępna tylko w obrębie modułu programu, w

którym ją zadeklarowano. Funkcję taką nazywamy statyczną. Domyślnie

przyjmowany jest specyfikator extern .

Ad. 2.

Typ wartości - te same reguły co w przypadku deklaracji - może być on

dowolny z wyjątkiem funkcji i tablicy. Dozwolony jest więc specyfikator void,

kiedy pozwala definiować funkcję nie zwracającą wartości. Domyślnie

przyjmowany jest int.

background image

Ad. 3.

Przy tworzeniu nazw funkcji obowiązują te same zasady, co w przypadku

identyfikatorów zmiennych. W języku C w obrębie danego modułu nazwy

funkcji nie mogą się powtarzać.

Ad. 4.

Lista deklaracji parametrów składa się z deklaracji parametrów rozdzielonych

przecinkami. Deklaracja parametru wygląda identycznie, jak deklaracja

zmiennej typu takiego samego, jak parametr: np.:

INT f(int i) {...}

Funkcja f posiada parametr całkowity i oraz parametr pr wskazujący funkcję

Ad. 6.

Ciało funkcji zbudowane jest z deklaracji i instrukcji, całość ujęta w nawiasy

klamrowe. Przed nawiasem zamykającym } zapisuje się instrukcję return.

Wykonanie instrukcji return w bloku funkcji wywoływanej lub wykonanie

ostatniej instrukcji w tym bloku zwraca sterowanie do funkcji wywołującej.

Definiowanie funkcji zastępuje prototyp, tzn. dostarcza informacji niezbędnych

dla kontroli poprawności odwołań do funkcji, ale nie odwołanie - prototyp nie

pełni roli definicji.

4.6.3. Wywołanie funkcji

Wywołanie funkcji ma postać:

identyfikator_funkcji (lista_parametrów_wywołania)

Na listę parametrów wywołania składają się oddzielone przecinkami wyrażenia

typu zgodnego z typem parametru funkcji.

Przykładowe wywołanie funkcji func zdefiniowanej jako:

INT func(INT i, INT j, CHAR *str)

{

...

}

może mieć postać:

background image

INT a=10, b=20;

CHAR *tekst=”Programowanie w jezyku C”;

func(a, b, tekst);

/* wywołanie funkcji func */

W tym przykładzie typy parametrów wywołania są identyczne z typami

określonymi w definicji funkcji.

5. Standardowa biblioteka funkcji

5.1. Standardowe wejście i wyjście

Wszystkie programy odwołujące się do funkcji ze standardowej biblioteki

języka C muszą mieć dołączony plik nagłówkowy tej biblioteki, zawierający

definicje wielu zmiennych oraz makrodefinicje. Plik ten nazywa się stdio.h i

jest dołączony do programu dyrektywą preprocesora:

#include <stdio.h>

W języku C wszystkie urządzenia zewnętrzne są traktowane tak jak pliki.

Wynika to z przyjęcia założenia, że wszystkie strumienie danych (do / z pliku) i

wszystkie pliki można traktować tak samo, chociaż niektóre strumienie / pliki

danych mogą przypływać z różnych kierunków, np. z dysku, z kasety, z

terminala, a nawet od drukarki. W pliku nagłówkowym stdio.h zdefiniowano

trzy strumienie plikowe (file stream) standardowo wstępnie związane z

urządzeniami zewnętrznymi:

stdin – standardowe wejście do odczytu (pre-opened input)

stdout – standardowe wyjście do zapisu (pre-opened standard output)

stderr – standardowe wyjście diagnostyczne, dla komunikatów o błędach

(standard error)

Zazwyczaj stdin zostaje powiązany z klawiaturą, a stdout i stderr zostają

powiązane z ekranem monitora.

Podstawowe funkcje obsługi wejścia / wyjścia, to getchar i putchar.

background image

5.1.1. Funkcje getchar() i putchar()

Funkcja getchar() pobiera jeden znak ze standardowego wejścia i zwraca jego

kod jako swoją wartość, natomiast putchar wyświetla znak o podanym kodzie.

Obie funkcje zwracają wartość –1 w przypadku wystąpienia błędu odczytu lub

zapisu.

Funkcja getchar() jest wywoływana poprzez przypisanie jej (znakiem równości

= ) do jakiejś zmiennej. Funkcja ta nie ma argumentu. Oznacza to, że nic nie jest

przekazywane funkcji w nawiasach okrągłych. Specyfiką wprowadzania

wartości przy wykonywaniu funkcji getchar() jest to, że po naciśnięciu klawisza

i wyświetleniu wybranego znaku na ekranie nie potrzeba naciskać klawisza

ENTER. Program kontynuuje działanie natychmiast po wprowadzeniu znaku i

jest on przypisywany zmiennej już po naciśnięciu klawisza.

Funkcja putchar() wyprowadzająca znak na ekran wymaga podania jako

argument – znaku tj. zmiennej typu char albo zmiennej całkowitej - wartość

kodu znaku zwróconego przez funkcję getchar().

Poniżej przedstawiono program obrazujący sposób stosowania funkcji getchar()

i putchar():

#include <stdio.h>

main()

{

char znak;

puts(”Nacisnij klawisz alfanumeryczny:”);

znak=getchar();

putchar(‘\n’); /* nowy wiersz */

putchar(znak);

}

background image

Z funkcji getchar() można również skorzystać także, aby zatrzymać działanie

programu w celu np. odczytania pojawiających się wyników obliczeń na

ekranie. Po wypełnieniu ekranu napisami (zwykle 25 wierszy) można zatrzymać

program do czasu naciśnięcia dowolnego klawisza przez użytkownika. Efekt ten

uzyska się stosując następujący zapis:

puts(”Nacisnij klawisz aby kontynuowa

ć

”);

getchar();

Naciśnięcie klawisza spowoduje uruchomienie programu, ale żadna wartość nie

zostanie przypisana zmiennej.

5.1.2. Funkcje gets() i puts()

Funkcja gets() pobiera linię (łańcuch) ze standardowego wejścia i zwraca

wskaźnik na bufor zawierający tę linię. Składnia funkcji gets() jest następująca:

#include <stdio.h>

char *gets(char *s);

gdzie s oznacza identyfikator tablicy znakowej, w której przechowywane są

znaki wczytane z standardowego wejścia. Funkcja gets() kończy swoje działanie

po wczytaniu znaku przejścia do nowego wiersza (po naciśnięciu ENTER), albo

po wczytaniu znaku EOF (koniec pliku). Funkcja dodaje do tablicy znakowej

znacznik ‘\0’ i zwraca s – wskaźnik do tablicy, do której zapisała znaki, albo

wskaźnik pusty (null pointer), jeśli operacja się nie udała.

Funkcja puts() wyprowadza linię znaków (łańcuch) do standardowego

strumienia wyjściowego stdout. Składnia funkcji puts() jest następująca:

#include <stdio.h>

char puts(char *s);

gdzie s oznacza tablicę (lub wskaźnik) odwołujący się do łańcuch znakowego,

który ma zostać wysłany do standardowego strumienia wyjściowego.

Funkcja puts() likwiduje znacznik końca tekstowego, dodając w jego miejsce

znak przejścia do nowego wiersza.

background image

Poniżej przedstawiono program obrazujący sposób stosowania funkcji gets() i

puts():

#include <stdio.h>

main()

{

char imie[10];

puts(”Podaj swoje imie:”);

gets(imie);

putchar(‘\n’); /* nowy wiersz */

puts(”Twoje imie to:”);

putchar(‘\n’); /* nowy wiersz */

puts(imie);

return 0;

}

Podczas wprowadzania znaków możliwe jest usuwanie błędnych znaków

klawiszem Backspace i ponowne ich wpisywanie. Zaakceptowanie wpisanych

znaków następuje po naciśnięciu Enter. Zostają one wówczas uzupełnione o

znak ogranicznika łańcucha i przypisane danej zmiennej.

5.1.3. Funkcje scanf() i printf()

Funkcja scanf() jest uniwersalnym sposobem na wprowadzanie do komputera

danych wszelkiego typu. Funkcja śledzi (skanuje) klawiaturę badając, czy został

naciśnięty jakiś klawisz, a potem interpretuje dane wejściowe zgodnie ze

specyfikatorami formatowania. Składnia funkcji scanf() jest następująca:

#include <stdio.h>

int scanf(const char *format, [adres,...]);

Parametr funkcji jest dwuczęściowy – składa się z łańcucha sterującego (format

string) i listy danych. Łańcuch sterujący zawiera specyfikatory formatowania

wskazujące, jak będą interpretowane dane wejściowe.

background image

Lista specyfikatorów formatowania dla funkcji scanf():

%d wprowadza liczbę całkowitą

%u wprowadza liczbę bez znaku

%f wprowadza daną typu float

%e wprowadza liczbę w zapisie wykładniczym

%g wprowadza liczbę dziesiętną w najkrótszym zapisie dziesiętnym lub

wykładniczym

%c wprowadza daną typu char

%s wprowadza łańcuch

Specyfikatory formatowania są nazywane znakami konwersji, ponieważ

przekształcają dane pierwotne w strumieniu wejściowym na dane zgodne z

wybranymi typami.

W przypadku użycia specyfikatora formatu %s wczytywanie danych zostanie

zakończone, jeśli w strumieniu znaków wejściowych pojawi się jeden z znaków

specjalnych: spacja, znak nowego wiersza, tabulator. Znaki wczytywane przez

funkcje są przechowywane w tablicy znakowej, która musi zostać wskazana

funkcji, jako jej argument. Po zakończeniu wczytywania łańcuch znaków jako

ostatni element tablicy znakowej zostaje automatycznie dodany znacznik ‘\0’.

W liście danych z każdym użytym specyfikatorem formatu musi być związany

kolejny argument będący wskaźnikiem na zmienne właściwego typu. W tym

celu wykorzystywany jest operator pobrania adresu &. Jest on używany do

wprowadzania danych numerycznych i typu char, nie stosuje się go w

przypadku zmiennych łańcuchowych. Poniżej zamieszczono program ilustrujący

wykorzystanie funkcji scanf() do wprowadzania zmiennych różnych typów:

#include <stdio.h>

main()

{

char nazwisko[15]

int wiek;

background image

float stan_konta;

char plec;

puts(”Podaj nazwisko:”);

scanf(”%s”,nazwisko);

putchar(‘\n’);

puts(”Podaj wiek:”);

scanf(”%d”,&wiek);

putchar(‘\n’);

puts(”Podaj stan konta:”);

scanf(”%f”,&stan_konta);

putchar(‘\n’);

puts(”Plec: wybierz <m> lub <k>”);

scanf(”%c”,&plec);

putchar(‘\n’);

}

Funkcja printf() służy do wyprowadzania komunikatów i wartości zmiennych

na ekranie. Składnia funkcji printf() jest następująca:

#include <stdio.h>

int printf(const char *format_string, ...);

Argument format_string to łańcuch znaków zawierający specyfikatory formatu

analogiczne jak dla funkcji scanf(). Wielokropek ... oznacza sekcję

przeznaczoną dla ewentualnych wyrażeń, które powinny zostać sformatowane

zgodnie ze specyfikatorami formatu. Przykładem zastosowania funkcji printf()

może

być

fragment

programu

stanowiący

uzupełnienie

programu

przedstawionego powyżej -ilustrującego użycie funkcji scanf().

printf(”Nazwisko: %s\n”,nazwisko);

printf(”Wiek:%d\n”,wiek);

printf(”Stan konta:%f”,stan_konta);

printf(”Plec:%c”,plec);

background image

5.2. Obsługa plików

Wyprowadzenie danych przez program w języku C np. na dyskietkę lub

drukarkę odbywa się poprzez przesłanie ich do specjalnego obszaru pamięci

nazywanego buforem. Aby przeniesienie informacji do i z buforu było możliwe,

musi istnieć łącze komunikacyjne między programem a systemem operacyjnym

komputera. Tym łączem jest plik. Pojęcie pliku w języku C może więc odnosić

się do pliku dyskowego (disk file), urządzenia peryferyjnego typu terminal lub

drukarki. Plik przyporządkowany jest do konkretnego urządzenia, z którym

program dokonuje wymiany informacji. Przed rozpoczęciem tej wymiany

należy otworzyć ten plik, a po zakończeniu wymiany należy koniecznie

zamknąć plik.

Zasób informacji przepływających z programu do pliku lub odwrotnie

nazywany jest strumieniem danych (stream). Strumień danych jest niezależny

od urządzenia (device-independent). Aby przeprowadzić operację Wejścia /

Wyjścia, należy skojarzyć plik ze strumieniem, by dokonywać odczytu danych z

pliku, bądź zapisu danych do pliku, przy czym może to być plik dowolnego

typu. Istnieją dwa formaty strumieni: tekstowy (text stream) i binarny (binary

stream). Strumienie tekstowe są stosowane do przesyłania danych tekstowych z

jednego środowiska do innego, strumienie binarne dotyczą danych nie-

tekstowych wymagających dokładnego przesłania zawartości pliku (z

zachowaniem różnych kodów specjalnych).

Aby zadeklarować plik korzysta się ze składni:

FILE *file_pointer;

Wskaźnik file_pointer jest nazywany wskaźnikiem plikowym i służy on do

odwoływania się do konkretnego pliku dyskowego. Słowo kluczowe file

oznacza, że zmienna wskazuje na strukturę pliku. Deklaracja może dotyczyć

więcej niż jednego pliku, np.:

FILE *plik_wejscia, *plik_wyjscia;

background image

5.2.1. Otwieranie pliku

Otwarcie pliku i skojarzenie go z strumieniem danych dokonywane jest za

pomocą funkcji bibliotecznej fopen(). Funkcja ta wymaga podania dwóch

argumentów: trybu otwarcia pliku oraz nazwy konkretnego pliku,

przeznaczonego do otwarcia. Nazwa pliku musi spełniać konwencje związane z

nazywaniem plików obowiązujące w danym systemie operacyjnym.

Przykładowo, w systemie DOS długość nazwy nie może przekraczać 8 znaków

z opcjonalnym 3-znakowym rozszerzeniem. W celu wydrukowania danych

wyjściowych na drukarce (zamiast wysyłać je do pliku na dysku) należy jako

nazwy pliku użyć "PRN" (w cudzysłowach).

Uogólniony format funkcji fopen() jest następujący:

#include<stdio.h>

FILE *fopen(const char *filename, const char *mode);

gdzie:

filename – nazwa pliku do otwarcia, wskaźnik do stałego łańcucha znaków,

mode – tryb otwarcia pliku.

Poniżej przedstawiono listę dostępnych wartości łańcucha mode:

r” - otwiera istniejący plik tekstowy, tylko do odczytu (read),

w” - tworzy nowy plik tekstowy do zapisu (write),

a” - otwiera istniejący plik tekstowy w trybie dopisywania (append),

r+” - otwiera istniejący plik tekstowy do odczytu i/lub zapisu,

w+” - tworzy plik tekstowy do zapisu i/lub odczytu,

a+” - otwiera lub tworzy plik tekstowy do dopisywania na końcu,

rb” - otwiera istniejący plik binarny do odczytu,

wb” - tworzy nowy plik binarny do zapisu,

ab” - otwiera istniejący plik binarny w trybie dopisywania do końca pliku,

r+b” - otwiera istniejący plik binarny dla odczytu i/lub zapisu,

w+b”- tworzy plik binarny do zapisu i/lub odczytu,

a+b” – otwiera lub tworzy plik binarny do dopisywania na końcu.

background image

Funkcja fopen() zwraca wskaźnik typu FILE. Jeśli wystąpi błąd podczas

otwierania pliku, funkcja zwraca wskaźnik pusty (null pointer);

Fragment programu otwierającego plik tekstowy tylko do odczytu

przedstawiono poniżej:

#include <stdio.h>

main()

{

FILE *plik;

if ((plik=fopen(”DANE.DTA”,”r”))==NULL)

{

printf(”Nie mozna otworzyc pliku \n);

exit(1)

}

}

Funkcja exit() zwraca do systemu operacyjnego wartość. Ponieważ jest to

wartość niezerowa, to do systemu zostaje przekazana informacja o

nieprawidłowym zakończeniu (abnormal termination / program aborted).

Przyczyną powstania błędu podczas otwarcia pliku może być brak miejsca na

dysku, niewłaściwa nazwa pliku lub próba odczytu nie istniejącego pliku.

5.2.2. Zamykanie pliku

Po wykonaniu operacji odczytu z pliku dyskowego, bądź zapisu do pliku

dyskowego należy koniecznie zamknąć plik. Zapewnia to zachowanie danych

aktualnie znajdujących się w buforze. Jeżeli plik nie zostałby prawidłowo

zamknięty w chwili odłączenia wskazanego strumienia danych od pliku, to

część danych może zostać bezpowrotnie utracona. Zamknięcia pliku wraz z

czyszczeniem bufora (flush) dokonuje funkcja biblioteczna fclose().

Uogólniony format funkcji fclose() jest następujący:

background image

#include <stdio.h>

int fclose(FILE *file_pointer)

gdzie:

file_pointer – oznacza wskaźnik skojarzony ze strumieniem do otwartego pliku.

Jeśli operacja zamknięcia pliku powiedzie się, funkcja zwraca 0, w przeciwnym

razie zwraca kod EOF.

5.2.3. Funkcje zapisu i odczytu

Do odczytu danych z pliku i zapisu do pliku służą odpowiednio standardowe

funkcje biblioteczne fgetc() i fputc(). Składnia funkcji fgets() jest następująca:

#include <stdio.h>

int fgetc(FILE *plik);

gdzie:

plik – oznacza wskaźnik do pliku, który jest skojarzony ze strumieniem danych.

Funkcję getc() można wykorzystać do odczytu z pliku danych w trybie znak-po-

znaku.

Uogólniona składnia funkcji putc() jest następująca:

#include <stdio.h>

int fputc(int c, FILE *plik);

gdzie:

int c – oznacza wartość numeryczną – kod znaku c.

plik – oznacza wskaźnik do pliku, który jest skojarzony ze strumieniem danych.

Funkcja zwraca wyprowadzony do pliku znak, jeśli operacja zakończyła się

sukcesem, w przeciwnym razie zwrócony zostanie kod EOF. Po dokonaniu

zapisu znaku do pliku funkcja dodatkowo przesuwa o 1 pozycję w przód

skojarzony z danym plikiem znacznik pozycji.

Poniżej zamieszczono program, który otwiera istniejący na dysk plik tekstowy o

nazwie „Dane.dta”, wczytuje z niego kolejne znaki (aż do napotkania znaku

EOF) i zapisuje je do nowo utworzonego pliku o nazwie „Wyniki.wyn”.

background image

#include <stdio.h>

main()

{

FILE *plik1,*plik2

char nazwa_pliku1[]=”dane.dta”;

char nazwa_pliku2[]=”wyniki.wyn”;

char znak;

if ((plik1=fopen(nazwa_pliku1,”r”))==NULL

{

printf (”Nie mo

ż

na otworzyc pliku danych \n”);

exit(1);

}

else

if ((plik2=fopen(nazwa_plik2,”w”))=NULL

{

printf(”Nie mo

ż

na utworzyc pliku wynikow \n);

exit(1);

}

else while((znak=fgetc(plik1))!=EOF)

{

putchar(znak); /*wydruk znaku na ekran */

fputc(znak,plik2);

}

fclose(plik1);

fclose(plik2);

return(0);

}

background image

Do odczytu z pliku i zapisu w pliku jednocześnie całego wiersza tekstu służą

funkcje biblioteczne fgets() i fputs().

Uogólniona składnia funkcji fgets() jest następująca:

#include<stdio.h>

char *gets(char *s,int n, FILE *plik);

gdzie:

s- referencja do tablicy znakowej uzywanej do zapamiętania znaków

wczytanych z otwartego pliku wskazywanego przez wskaźnik,

n – maksymalna ilość elementów tablicy znakowej.

Funkcja zwraca wskaźnik char *s jeśli operacja wczytania zakończyła się

sukcesem. Jeżeli napotkany zostanie kod EOF, funkcja zwraca pusty wskaźnik i

pozostawia tablicę w stanie poprzednim. Jeżeli wystąpi błąd wczytywania

funkcja zwraca pusty wskaźnik, ale zawartość tablicy jest nieokreślona.

W przypadku napotkania znaku przejścia do nowego wiersza (‘\0’) funkcja

włącza go do wyjściowej tablicy znakowej.

Uogólniona składnia funkcji fputs() jest następująca:

#include <stdio.h>

int fputs(const char *s, FILE *plik);

gdzie: s – wskazuje tablicę znakową, w której znajduje się tekst przeznaczony

do zapisu w pliku dyskowym skojarzonym ze wskaźnikiem plikowym *plik.

Modyfikator const wskazuje, że zawartość tablicy znakowej wskazywanej przez

identyfikator s nie może się zmieniać. W przypadku błędu funkcja zwraca

wartość niezerową, w przypadku sukcesu – zero.

Poniżej zamieszczono program, którego wynik działania jest podobny do efektu

działania programu zamieszczonego poprzednio. Otwarty zostaje istniejący na

dysku plik tekstowy o nazwie „Dane.dta”, a następnie wczytywane są z niego są

kolejne linie tekstu (o długości maksymalnej 81 znaków), aż do napotkania

znaku EOF. Poszczególne wiersze zostają zapisane w nowo utworzonym pliku o

nazwie „Wyniki.wyn”.

background image

#include <stdio.h>

#define linia 81;

main()

{

FILE *plik1,*plik2

char nazwa_pliku1[]=”dane.dta”;

char nazwa_pliku2[]=”wyniki.wyn”;

char tablica[linia];

if ((plik1=fopen(nazwa_pliku1,”r”))==NULL

{

printf (”Nie mo

ż

na otworzyc pliku danych \n”);

exit(1);

}

else

if ((plik2=fopen(nazwa_plik2,”w”))=NULL

{

printf(”Nie mo

ż

na utworzyc pliku wynikow \n);

exit(1);

}

else while((fgets(tablica,linia,plik1))!=NULL)

{

printf(”%s”,tablica); /*wydruk znaku na ekran */

fputs(tablica,plik2);

}

fclose(plik1);

fclose(plik2);

return(0);

}

background image

Istnieje mozliwość jednorazowego odczytania i zapisania bloku danych z/do

pliku dyskowego. Do wykonania blokowych operacji wejścia/wyjścia służą

funkcje biblioteczne fread() i fwrite().

Uogólniona składnia funkcji fread() jest następująca:

#include <strdio.h>

size_t fread(void *ptr, size_t size, size_t n,FILE *plik);

gdzie ptr oznacza wskaźnik do tablicy, w której są przechowywane dane

wczytane. Liczba size określa rozmiar każdego elementu tablicy, n określa

liczbę elementów do odczytu, a plik oznacza wskaźnik skojarzony z plikiem

otwartym do odczytu. Typ size_t to typ numeryczny całkowity zdefiniowany w

pliku nagłówkowym stdio.h. Funkcja fread() zwraca liczbę elementów, które

wczytała.

Uogólniona składnia funkcji fwrite() jest następująca:

#include <strdio.h>

size_t fwrite(void *ptr, size_t size, size_t n,FILE *plik);

gdzie:

ptr oznacza wskaźnik do tablicy, w której przechowywane są dane, które należy

zapisać do otwartego pliku wskazywanego przez wskaźnik plik. Parametr size

określa wielkość elementów tablicy. Argument n określa ilość elementów

tablicy, które należy zapisać do pliku. Funkcja zwraca liczbę rzeczywiście

zapisanych do pliku elementów.

Stosując funkcje fread(), fwrite() można zapisywać w plikach dowolne dane.

Najczęściej z ich wykorzystaniem zapisuje się struktury.

Ponieważ obliczenie parametru size może być kłopotliwe, można skorzystać z

funkcji bibliotecznej sizeof().

Wykonanie funkcji

size(structure_variable)

spowoduje obliczenie

wielkości struktury automatycznie.

Poniżej przedstawiono program ilustrujący sposób zapisu i odczytu struktury u z

wykorzystaniem funkcji fwrite() i fread().

background image

#include <stdio.h>

main()

{

FILE *plik;

int nr, lista_startowa;

struct zawodnicy

{

int numer;

char kraj[10];

char nazwisko[20];

float dlugosc_skoku;

} zawodnik;

char nazwa_pliku;

printf(”Ilu skoczkow startowalo ?”);

scanf(”%d,&lista_startowa);

printf(”Podaj nazwe pliku, który chcesz utworzyc: ”);

gets(nazwa_pliku);

if ((plik=fopen(nazwa_piku,”w”))==NULL)

{

printf(”Nie mo

ż

na otworzyc pliku \n”);

exit();

}

for(nr=1;nr<=lista_startowa;nr++)

{

zawodnik.numer=nr;

printf(”Podaj dane skoczka o numerze : %d”,&nr);

printf(”Kraj : ”);

gets(zawodnik.kraj);

background image

printf(”Nazwisko skoczka: );

gets(zawodnik.nazwisko);

printf(”Dlugosc skoku:”);

scanf(%f,&zawodnik.dlugosc_skoku);

fwrite(&zawodnik, sizeof(zawodnik), 1, plik);

}

fclose(plik);

while(fread(&zawodnik, sizeof(zawodnik), 1, plik)==1)

{

printf(”Numer : %d\n”,zawodnik.numer);

printf(”Kraj: %s\n”,zawodnik.kraj);

printf(”Nazwisko: %s\n”,zawodnik.nazwisko);

printf(”Dlugosc skoku: %f\n”,zawodnik.dlugosc_skoku);

}

fclose(plik);

}

Odczyt lub zapis pliku odbywa się zawsze począwszy od bieżącego położenia

tzw. wskaźnika pliku. Położenie tego wskaźnika można zmieniać funkcją

fseek():

int fseek(FILE *plik, long offset, int relation);

gdzie:

plik – oznacza wskaźnik plikowy skojarzony z otwartym plikiem,

offset – oznacza przesunięcie w bajtach od punku określonego przez trzeci

argument,

relation – punkt odniesienia, od którego rozpoczyna się odliczanie.

Przykładowo, w celu przesunięcia wskaźnika pliku o pięć rozmiarów zmiennej

zawodnik względem początku pliku, czyli ustawienia go na szóstym zapisie tego

typu należy wykonać instrukcje:

fseek(plik, (long) (5*sizeof(zawodnik)), 0);

background image

Aby odczytać bieżącą znacznika w pliku, można w każdej chwili posłużyć się

funkcją biblioteczną ftell(). Składnia funkcji ftell() jest następująca:

#include <stdio.h>

long ftell (FILE *plik)

Funkcja zwraca bieżącą wartość pozycji wskaźnika w pliku liczoną do bieżącej

pozycji w bajtach od początku pliku.

Przydatną w praktyce jest funkcja rewind(), która cofa znacznik na sam

początek pliku. Składnia tej funkcji jest następująca:

#include <stdio.h>

void rewind (FILE *plik)

Ostatnią parę funkcji odczytu/zapisu danych tworzą funkcje fscanf() i fprintf().

Pośród funkcji bibliotecznych C obsługujących operacje wejścia/wyjścia są one

odpowiednikami funkcji scanf() i printf(), ale dodatkowo pozwalają

programiście wybrać strumień danych wejścia i wyjścia. Ponieważ ich składnia

różni się tylko pierwszym argumentem oznaczającym wskaźnik plikowy

skojarzony z otwartym plikiem od składni funkcji omówionych wcześniej, nie

będą one szczegółowo omawiane.

5.3. Manipulowanie łańcuchami znakowymi

Funkcje operujące na łańcuchach znaków zostały zestawione w pliku

nagłówkowym string.h. Standard języka ANSII C zawiera następujące funkcje

biblioteczne obsługujące łańcuchy:

Funkcja

Opis

strcat

Dodaje znaki jednego łańcucha na końcu innego

strchr

Oblicza położenie wybranego znaku w łańcuchu

strcmp

Porównuje dwa łańcuchy

strcmpl

Porównuje dwa łańcuchy bez rozróżniania wielkich i

małych liter

strcpy

Kopiuje jeden łańcuch do innego

background image

strdup

Tworzy kopię łańcucha

strlen

Oblicza długość łańcucha

strlwr

Zamienia litery w łańcuchu na małe

strncat

Dołącza na końcu jednego łańcucha wybrane znaki z

innego łańcucha

strncmp

Porównuje wybrane znaki w dwóch łańcucach

strncpy

Kopiuje wybrane znaki z łańcucha do łańcucha

strnset

Zamienia wybrane znaki w łańcuchu na inne

strrev

Odwraca kolejność znaków w łańcuchu

strstr

Odnajduje łańcuch w innym łańcuchu

strupr

Zamienia litery w łańcuchu na wielkie

Poniżej zamieszczono program, który tłumaczy sposób stosowania niektórych

funkcji bibliotecznych manipulowania łańcuchami znakowymi.

#include <stdio.h>

#include <string.h>

main()

{

char imie1[]={‘A’,’D’,’A’,’S’,’ ‘,’\0’};

char imie2[]=”Andrzej ”;

char *nazwisko1=”Kysiak”;

char nazwisko2[];

printf(”Dlugosc lancucha=%d”,strlen(imie1));

strcpy(nazwisko2,nazwisko1); /*kopiowanie lancucha*/

strlwr(imie1); /*zamienia litery lancucha na male*/

imie1[1]=imie2[1];

strcat(imie1,nazwisko1); /*laczy dwa lancuchy*/

strcat(imie2,nazwisko2);

background image

printf(”%s\n”,imie1);/*wyswietla napis: Adas Kysiak*/

printf(„%s\n,imie2); /*napis: Andrzej Kysiak*/

return 0;

}

5.4. Funkcje matematyczne

Wszystkie funkcje matematyczne zostały zamieszczone w pliku nagłówkowym

math.h, który należy dołączyć do programu dyrektywą #include. Najczęściej

stosowane funkcje matematyczne należą do grupy funkcji trygonometrycznych i

hiperbolicznych oraz do grupy funkcji wykładniczych i logarytmicznych.

5.4.1. Funkcje trygonometryczne

Uogólniony format dla funkcji trygonometrycznych jest następujący:

#include <math.h>

double sin(double x);

double cos(double x);

double tan(double x);

Argumentem funkcji trygonometrycznej jest zmienna x o wartości typu double,

która wyraża kąt mierzony w radianach. Funkcje zwracają wartość również typu

double.

Przykład stosowania funkcji trygonometrycznych przedstawia program:

#include <math.h>

#define PI 3.141593

main()

{

double x;

printf(”Podaj wartosc kata w stopniach:”);

scanf(”%d,&x);

x*=PI/180.0; /*zamiana stopni na radiany*/

background image

printf(”sinus kata %f wynosi: %f.\n”,x,sin(x));

printf(”kosinus kata %f wynosi: %f.\n”,x,cos(x));

printf(”sin hiperb. kata%f wynosi: %f.\n”,x,sinh(x));

return 0;

}

5.4.2. Funkcje wykładnicze i logarytmiczne

Najczęściej stosowane funkcje wykładnicze to: pow(),exp() i sqrt(). Składnia tyc

funkcji jest następująca:

#include <math.h>

double pow(double a, double b);

double exp(double b)’

double sqrt(double x);

gdzie: a – to podstawa w operacji potęgowania, b to wykładnik potęgi,

natomiast x to liczba podpierwiastkowa. Funkcja pow() wykonuje operację

podnoszenia do potęgi a

b

, funkcja exp() wykonuje operację e

b

, natomiast funkcja

sqrt() zwraca pierwiastek kwadratowy ze swojego nieujemnego argumentu x (w

przeciwnym przypadku zwraca błąd). Wyniki zwracane przez funkcje są w

formacie double.

Funkcje logarytmiczne log() i log10() wykonują operacje wyznaczania

logarytmów odpowiednio: naturalnego i dziesiętnego dla argumentu o wartości

typu double. Poniżej zamieszczono program ilustrujący sposób użycia

omawianych funkcji:

#include <stdio.h>;

#include <math.h>

main()

{

double x10 = 100.0;

double e=2.71828;

background image

double wynik,wynik10;

wynik=log(e);

wynik10=log10(x10);

printf("Log natur. z e=%f wynosi %f\n",e,wynik);

printf("Log. dzies. z %f wynosi %f\n",x10,wynik10);

printf("e^x dla 1 wynosi %f\n",2.0,exp(1));

return 0;

}

5.5. Funkcje czasu i daty

Wszystkie funkcje odnoszące się do daty i czasu są zawarte w pliku

nagłówkowym time.h. Funkcje te mogą dać trzy rodzaje danych odnoszących

się do daty i czasu:

czas kalendarzowy – calendar time

czas lokalny – local time

czas zimowy/ letni – daylights savings time.

Funkcja time() w języku C zwraca czas kalendarzowy. Składnia funkcji jest

następująca:

#include <time.h>

time_t time(time_t *timer);

gdzie: time_t jest to tzw. typ arytmetyczny stosowany do zapisu czasu,

natomiast timer to zmienna typu wskaźnik – wskazująca miejsce w pamięci, w

którym powinny zostać zapamiętane dane dotyczące daty i czasu zwrócone

przez funkcję.

Funkcja localtime() zwraca czas lokalny po przekształceniu czasu

kalendarzowego.

#include <time.h>

struct tm *localtime(time_t *timer);

background image

gdzie:

tm

oznacza

strukturę

danych,

zawierającą

składniki

czasu

kalendarzowego. Struktura ta jest zdefiniowana następująco:

struct tm {

int tm_sec; /* sekundy [0-59] */

int tm_min; /* minuty [0-59] */

int tm_hour; /* godziny [0-23] */

int tm_mday; /* dzien [1-31] */

int tm_mon; /* miesiac[0-11] ,

0 – stycze

ń

, itd. */

int tm_year; /* rok (od 1900) */

int tm_wday; /* dzien tygodnia[0-6],

0 – niedziela, itd. */

int tm_yday; /* dzien w roku [0-365] */

int tm_isdst;/* znacznik trybu liczenia godzin:

0 – dwunastogodzinny (am i pm)

1 - dwudziestoczterogodzinny] */

};

Aby przekształcić dane dotyczące daty i czasu przechowywane w strukturze tm

w łańcuch znaków – wygodny i bardziej czytelny dla użytkownika, należy

wywołać funkcję asctime() dokonującą konwersji na łańcuch znaków ASCII.

Uogólniona składnia tej funkcji jest następująca:

#include<time.h>

char *asctime(const struct tm *timeptr);

gdzie: timeptr oznacza wskaźnik odwołujący się do struktury tm zwróconej

przez funkcję localtime(). Funkcja asctime() zamienia te dane na łańcuch

znaków i zwraca wskaźnik do tego łańcucha. Przykład odczytu aktualnej daty i

czasu przedstawia program:

background image

#include <time.h>

#include <stdio.h>

main()

{

time_ t czas;

time(&czas);

printf(„Aktualna data i czas: %s”,

asctime(localtime(&czas)));

}

5.6. Inne przydatne funkcje biblioteczne

Z bogatej biblioteki języka C wybrano kilka, które są używane w większości

programów:

exit() – powoduje zakończenie pracy programu. Zwykle funkcję tę wywołuje się

w sytuacjach błędnych, po wyświetleniu odpowiedniego komunikatu.

rand() – to generator liczb pseudolosowych. Funkcja zwraca wartość losową

typu int z zakresu od 0 do liczby max zadanej jako argument funkcji. Funkcja

srand() ustawia wartość początkową dla generatora.

qsort(ptr,cnt,size,fun) – powoduje sortowanie danych tworzących tablicę o cnt

elementach, której pierwszy bajt jest wskazywany przez ptr. Rozmiar elementu

jest określony przez size, a funkcja porównująca przez fun. Funkcja skojarzona z

parametrem fun musi być tak dobrana, aby dla elementów elm1 i elm2

rezultatem wywołania:

(*fun)(&elm1,&elm2)

była dana typu int o wartości –1 , 0 albo 1, zgodnie z następującym

zestawieniem:

-1 jeśli elm1<elm2

0 jeśli elm1=elm2

+1 jeśli elm1>elm2

background image

6. Metodyka programowania strukturalnego

Przedstawione w poprzednich rozdziałach elementy programów w języku C :

jednostki leksykalne, instrukcje i funkcje umożliwiają tworzenie programów

komputerowych. Najlepszą metodą tworzenia programów wykorzystującą

specyfikę języka C jest tzw. programowanie strukturalne. Istnieją dwie

metodyki programowania strukturalnego:

metoda Top-Down, czyli „od góry w dół” oraz

metoda Bottom-Up, czyli „od dołu w górę”.

Opierając się na metodzie „bottom – up” należy rozpracowując jakiś problem

rozwiązywać możliwie jak najmniejsze elementy tego zadania. W tym celu

definiujemy i opracowujemy funkcje realizujące każde spośród takich zadań

cząstkowych. Po tym, jak każda z tych funkcji zostanie napisana, uruchomiona i

przetestowana – zaczynamy je łączyć razem tworząc program, który rozwiązuje

problem metodą zadań cząstkowych.

W metodzie top-down postępujemy odwrotnie. Tworzony jest główny program,

tzn. piszemy kod dla funkcji main(), deklarując jedynie funkcje, które mają

rozwiązywać kolejne zadania (ale jeszcze ich nie definiując). Dopiero po

opracowaniu struktury programu głównego przechodzi do definiowania

poszczególnych funkcji.

W praktyce często stosuje się postępowanie będące połączeniem tych dwóch

metod. Jednak zawsze działanie to oparte jest zasadzie podziału problemu na

zagadnienia cząstkowe - funkcje realizujące kolejne zadania. Dla pełnego

zrozumienia reguł stosowania funkcji w programowaniu w języku C konieczne

są jeszcze pewne wyjaśnienia związane z przekazywaniem parametrów i

zwracaniem wartości przez funkcje. Omówienia wymagają również pojęcia

zmiennych lokalnych i globalnych (zewnętrznych).

background image

6.1. Zasady definiowania i korzystania z funkcji

Własne zdefiniowane funkcje umieszczamy po nawiasie zamykającym funkcji

main(). Każda definicja funkcji musi zawierać następujące elementy:

typ_danych_funkcji nazwa_funkcji (typ_danych1 argument1, ...)

{ /* nawias otwierający */

/* instrukcje tworzące tzw. ciało funkcji*/

return ( )

} /* nawias zamykający */

Przed pierwszym użyciem w programie funkcja powinna być zdefiniowana lub

co najmniej zadeklarowana. Deklaracja funkcji określa interpretację i atrybuty

zestawu zmiennych funkcji, definicja funkcji natomiast wymaga od kompilatora

zarezerwowania pamięci dla danej funkcji. Deklaracja funkcji odwołuje się do

definicji tej funkcji, która znajduje się gdzieś, w innym miejscu, określa

natomiast jakiego typu wartość zostanie zwrócona przez tę funkcję. Definicja

określa czynności wykonywane przez funkcję podając jednocześnie liczbę i typy

argumentów przekazywanych do funkcji.

Zadeklarowanie funkcji nie jest równoznaczne z jej definicją. Jeśli definicja

funkcji jest umieszczona w tekście programu przed miejscem pierwszego

wywołania tej funkcji, nie trzeba deklarować tej funkcji. W przeciwnym razie

należy przed pierwszym jej wywołaniem umieścić w programie jej deklarację.

Funkcja może być zadeklarowana i zwracać dane dowolnego typu, za wyjątkiem

tablicy. Wartość zwracana przez użycie funkcji return() musi odpowiadać

zadeklarowanemu typowi funkcji. Jeżeli przed nazwą funkcji w deklaracji nie

zapisano typu funkcji, to domyślnie przyjmowany jest typ liczby całkowitej.

Dla zadeklarowania funkcji, która pobiera zmienną liczbę argumentów, należy

określić co najmniej pierwszy argument, a następnie użyć wielokropka,

reprezentującego resztę argumentów przekazywanych do funkcji.

W deklaracji funkcji, która nie pobiera żadnych argumentów, konieczne jest

wskazanie typu danych void.

background image

6.2. Zmienne lokalne i globalne

W programie składającym się z wielu funkcji występują zmienne, które

wykorzystywane są przez kilka funkcji oraz zmienne używane tylko przez

pojedyncze funkcje. Widoczność i dostępność tych zmiennych może być

ograniczona, a wartości przypisane tym zmiennym mogą być ukryte z punktu

widzenia wielu innych funkcji.

Jako zmienne globalne (program scope) przyjęto nazywać takie zmienne, które

można wykorzystywać w każdej funkcji programu. Zmienne, które są dostępne i

aktywne tylko pomiędzy początkiem, a końcem danej funkcji nazywa się

lokalnymi (function scope). Zasięg zmiennych globalnych obejmuje cały

program jeżeli są one zadeklarowane poza funkcją main(). Jeśli program będzie

składał się z wielu różnych plików to zmienne te będą również widoczne także

w tych pozostałych plikach. Taka grupa wielu plików dyskowych tworzących

razem zbiór plików źródłowych dla jednej aplikacji nazywa się projektem.

Zmienna lokalna jest zadeklarowana wewnątrz odrębnego bloku programowego.

W przypadku gdy zmienna jest zadeklarowana wewnątrz pewnej funkcji jej

zasięg obejmuje tylko ciało tej funkcji. Blok programowy może być określony

poprzez parę nawiasów klamrowych. W takim przypadku zmienna jest dostępna

od wiersza, w którym została umieszczona jej deklaracja – do końca bloku.

Zmienna lokalna istnieje tylko wtedy, kiedy jest wykonywana funkcja, w której

ją zadeklarowano. W chwili gdy rozpoczyna się wykonywanie funkcji, zmiennej

tej zostaje przydzielone miejsce w pamięci. Kiedy funkcja się kończy, pamięć

zajmowana przez zmienną jest zwalniana i zmienna przestaje istnieć. Można

uniknąć utraty wartości zmiennej przy zakończeniu wykonywania funkcji,

deklarując zmienną jako statyczną. Deklaracja przyjmuje wówczas postać:

moja_funkcja()

{

static int zmienna;

}

background image

Zmienna statyczna jest przypisana do stałego adresu w pamięci, tak długo, jak

długo trwa wykonywanie programu. Kiedy funkcja się kończy, adres nie zostaje

zwolniony, lecz jest dalej przypisany do zmiennej. Pozwala to na odzyskanie

wartości, która znajdowała się pod tym adresem, mimo że funkcja się

zakończyła.

Ponieważ zmienna lokalna jest ważna tylko wewnątrz funkcji, w której została

zadeklarowana, to można korzystać z nazwy tej zmiennej w innych funkcjach.

Jeżeli zmienna globalna i lokalna noszą taką samą nazwę, to w funkcji jest

wykorzystywana zmienna lokalna. Niemożliwe jest wykorzystywanie obydwu.

Wartość zmiennej globalnej jest zatem przesłonięta przez wartość zmiennej

lokalnej.

6.3. Przekazywanie parametrów do funkcji

Przy przekazywaniu parametrów do definiowanej funkcji można wyróżnić trzy

przypadki:

funkcja jest bezargumentowa,

funkcja pobiera stałą, określoną liczbę argumentów,

funkcja pobiera zmienną liczbę argumentów.

Dla wszystkich funkcji bezargumentowych w ich deklaracji stosowany jest typ

void, umieszczony w nawiasach. Przy wywołaniu w programie takiej funkcji

pomiędzy nawiasy nie można wówczas wstawić żadnego argumentu.

Deklaracja funkcji o stałej liczbie argumentów należy podać typy wszystkich

argumentów i ich nazwy. W wywołaniu takiej funkcji liczba przekazywanych

argumentów i ich typy muszą być zgodne z zadeklarowanymi. Kompilator

sprawdza poprawność i zgodność pomiędzy deklaracją i definicją funkcji oraz

wartościami przekazywanych argumentów w wywołaniu funkcji. Należy

również

zwracać

uwagę

na

zachowanie

odpowiedniej

kolejności

przekazywanych wartości argumentów.

background image

W języku C możliwe jest również deklarowanie prototypów funkcji o zmiennej

liczbie argumentów. Ogólny format deklaracji funkcji o zmiennej liczbie

argumentów jest następujący:

typ_danych nazwa_funkcji( typ_danych1 argument1, ...);

Wielokropek oznacza pozostałe argumenty, które nie zostały wymienione na

liście argumentów.

W pliku nagłówkowym stdarg.h zadeklarowano zostały trzy podprogramy

(routines), które pozwalają na tworzenie i obsługę funkcji o zmiennej liczbie

argumentów. Są to następujące makrorozkazy:

va_start()

va_arg()

va_end()

W pliku tym zdefiniowano również typ danych va_list, który określa typ tablicy

odpowiedni

do

przechowywania

danych

potrzebnych

do

działania

makrorozkazów.

Składnia makra va_start() obowiązuje składnia:

#include <stdarg.h>

void va_start(va_list tab, lastfix);

gdzie: tab – oznacza nazwę tablicy, która będzie zainicjowana w wyniku

działania makropolecenia va_start(). Identyfikator lastfix określa ostani

argument funkcji przed wielokropkiem w deklaracji funkcji.

Makropoloecenie va_arg() używane jest do pobrania kolejnego argumentu

przekazywanego do funkcji. Wywołania makra odbywa się wg. składni:

#include <stdarg.h>

void va_arg(va_list tab);

Aby zapewnić normalny i poprawny zwrot wartości przez funkcję o zmiennej

liczbie argumentów, należy zastosować makro va_end() po tym, jak wszystkie

argumenty zostaną przetworzone. Przykład zastosowania funkcji o zmiennej

liczbie argumentów przedstawia następujący program:

background image

#include <stdio.h>

#include <stdarg.h>

double dodaj_liczbe(int x, ...);

main()

{

double d1=1.5;

double d2=2.5;

double d3=3.5;

puts(”Dodawanie zmiennej liczby argumentow”);

printf(”Dany jest argument : %2.1f\n”,d1);

printf(”Wynik dodawania:%2.1f\n”,dodaj_liczbe(1,d1));

printf(”Dane sa argumenty: %2.1f, %2.1f\n”,d1,d2);

printf(”Wynik:%2.1f\n”,dodaj_liczbe(2,d1,d2));

printf(”Dane sa argumenty: %2.1f, %2.1f, %2.1f\n”,

d1,d2,d3);

printf(”Wynik: %2.1f\n”,dodaj_liczbe(3,d1,d2,d3));

return();

}

double dodaj_liczbe(int x, ...);

{

va_list lista;

int i;

double wynik=0.0;

va_start(lista,x);

for (i=0;i<x;i++)

wynik+=va_arg(lista,double);

va_end(lista);

return(wynik);

}

background image

7. Podstawy języka C++

7.1. Rozszerzenia C++ w stosunku do języka C

Język C++ jest obiektową pochodną języka C. Można przyjąć, że język jest

podzbiorem języka C++. Każdy program napisany w standardzie ANSI C jest

także programem w C++. Język C++ posiada wszystkie te funkcje jakie posiadał

standard języka C, ale wprowadza szereg nowych pojęć i związanych z tym

możliwości. Najważniejsze innowacje wprowadzone w języku C++ są

następujące:

wprowadzenie obiektów zdefiniowanych jako klasy zawierające nie tylko

definicje danych, które można w strukturach C, ale także deklaracje i

definicje funkcji, które działają na tych danych.

egzemplarze klas mogą być automatycznie inicjalizowane i usuwane za

pomocą konstruktorów i destruktorów.

dane zdefiniowane w klasie domyślnie są dostępne tylko funkcjom

składowym tej klasy. Kod zewnętrzny, który używa tej klasy, nie ma wpływu

na wewnętrzną implementację klasy.

dopuszczalne jest tzw. przeciążanie operatorów i funkcji. Oznacza to, że

można zdefiniować więcej niż jedną funkcję z tą samą nazwą, a dla danego

wywołania funkcji kompilator zidentyfikuje właściwą definicję.

charakterystyki typu klasy – dane i funkcje – mogą być dziedziczone przez

podklasy, nazywane także klasami pochodnymi.

C++ pozwala klasom na zdefiniowanie funkcji wirtualnych. Oznacza to

istnienie więcej niż jednej definicji funkcji, a decyzja o wyborze właściwej

funkcji jest podejmowana w trakcie pracy programu (tzw. polimorfizm).

mogą być zdefiniowane klasy szablonu, co pozwala na używanie różnych

egzemplarzy tej samej klasy z różnymi typami danych, ale przy nie

zmienionym kodzie.

background image

w C++ deklaracje nie muszą występować na pierwszym miejscu w funkcji,

ale mogą znajdować się pomiędzy innymi instrukcjami. Na przykład zmienna

x może być zadeklarowana i zainicjalizowana jako część instrukcji for:

include <iostream.h>

/*plik iostream.h to w C++ alternatywa stdio.h */

int main()

{

for (int x=5;x<10;x++);

return(0);

}

w programach C++ każda funkcja, która jest wywoływana przed swoją

definicją, musi być zadeklarowana za pomocą prototypu przed wywołaniem.

W języku ANSI C nie było to konieczne (chociaż zalecane dla funkcji, któ®e

zwracały wartość typu int).

wprowadzono różnicę w stosunku do ANSCI C w interpretacji prototypu

funkcji typu :

int funkcja();.

W standardzie języka C zapis ten

oznaczał, że

funkcja()

pobiera zero lub większą liczbę parametrów, w

języku C++ oznacza, że funkcja nie pobiera żadnych parametrów.

język C++ dostarcza mechanizm wywołania funkcji przez referencję. Użycie

typu referencyjnego zmiennej w wywoływanej funkcji powoduje, że zmiana

wartości zmiennej przekazywanej do danej funkcji powoduje również efekt

zmiany wartości tej zmiennej w funkcji wywołującej, np.:

#include <iostream.h>

void funkcja(int&);

int main()

{

int x=5;

funkcja(x);

printf(”x=%d,x); /*wyswietla x=10*/

background image

return(0);

}

void funkcja(int& x_ref);

{

xref=10;

}

w języku C wprowadzono kilka nowych słów kluczowych:

class

private

delete

protected

friend

public

inline

template

new

virtual

operator

śaden z używanych identyfikatorów zmiennych użytych w programie C++

nie może przyjmować nazwy zgodnej z jednym z wymienionych słów

kluczowych.

opracowano nową bibliotekę plikowego wejścia/wyjścia będącą alternatywą

dla standardowej biblioteki C w zdefiniowanej i zapisanej w pliku

nagłówkowym stdio.h.

7.2. Klasy i programowanie obiektowe

Pojęcie klasy wprowadzone w języku C++ jest uogólnieniem konstrukcji

struktury istniejącej w języku C. Podobnie jak struktura, klasa również posiada

składowe, które w odróżnieniu od struktury mogą być również funkcjami. W

sensie praktycznym pojęcie klasy łączy w sobie dane definiujące „przedmiot” i

metody określania jego właściwości. W ten sposób następuje integracja danych i

metod uprawnionych do ich wykorzystania. Przykładem deklaracji klasy może

być klasa KWADRAT, która zawiera zarówno dane definiujące kwadrat jak i

funkcję pozwalającą na jej narysowanie:

background image

class KWADRAT

{

int wsp_x[4],wsp_y[4];

public:

void Narysuj(void);

};

Taki sposób opisu „przedmiotu” jest charakterystyczny dla tzw. programowania

obiektowego. Pozwala ono na tworzenie programowych modeli przedmiotów

poprzez łączenie danych i metod uprzywilejowanych do ich wykorzystania.

Przedmioty – obiekty stają się głównym przedmiotem zainteresowania

programisty i jednocześnie podstawowym elementem konstrukcji programu.

Ogólna postać definicji klasy jest następująca:

słowo_kluczowe_klasy identyfikator_klasy

ciało_definicji_klasy

Definicja klasy rozpoczyna się od słowa_kluczowego_klasy, którym może być:

class, struct lub union. W ciele definicji klasy mogą znaleźć się:

- deklaracje danych składowych,

- deklaracje funkcji składowych,

- definicje funkcji składowych.

Wymienione elementy rozmieszcza się w sekcjach definicji klasy wg. schematu:

class Klasa

public:

...

/*funkcje i dane publiczne*/

protected:

...

/*funkcje i dane składowe zabezpieczone */

private:

...

/*funkcje i dane składowe prywatne */

};

background image

W sekcji prywatnej (private) składowe są widoczne tylko w obrębie funkcji

składowych danej klasy. Dane definiuje się jako prywatne wtedy, gdy zmiana

wartości składowej przez niepowołaną do tego funkcję zewnętrzną jest

ryzykowna lub zmianie tej powinno towarzyszyć wykonanie ściśle określonych

czynności. Funkcje definiuje się jako prywatne gdy ich wywołanie przez funkcje

zewnętrzne jest niewskazane lub gdy realizują pewien fragment algorytmu i ich

wywołanie z zewnątrz nie ma sensu.

W sekcji zabezpieczonej (protected) składowe są widoczne tylko w obrębie

funkcji składowych danej klasy i klas wyprowadzonych.

Składowe w sekcji publicznej (public) są widoczne w obszarze całego pliku.

Publiczne funkcje składowe służą zwykle do sterowania, zmiany parametrów i

komunikacji z obiektem. Dane składowe definiuje się jako publiczne w

przypadku prostych klas lub gdy zmiana zawartości pola nie musi być związana

z wykonaniem jakiś dodatkowych czynności.

Oprócz funkcji składowych definiowanych przez programistę w celu

opracowania jakiegoś obiektu łączącego w sobie dane „przedmiotu” i jego

właściwości, istnieją dwie dodatkowe funkcje specjalne: konstruktor i

destruktor. Funkcje te są albo definiowane albo generowane przez kompilator

(w przypadku ich braku). W C++ za pomocą konstruktorów i destruktorów

wykonywana jest automatyczna inicjalizacja i usuwanie obiektów.

Konstruktor to funkcja składowa klasy, która inicjuje zmienne tej klasy. Nazwa

funkcji konstruktora jest zawsze taka sama, jak nazwa klasy. Destruktor jest

funkcją składową klasy, która wykonuje operacje porządkujące, zanim

egzemplarz klasy ulegnie samozniszczeniu. Nazwa funkcji destruktora jest taka

sama, jak nazwa klasy z przedrostkiem w postaci znaku tyldy (~).

Funkcja konstruktora wywoływana jest jako część definicji egzemplarza klasy.

Destruktor wywoływany jest niejawnie, ale automatycznie, gdy zmienna

opuszcza zasięg. Poniżej przedstawiono definicję klasy z uwzględnieniem

użycia konstruktora i destruktora:

background image

class konto_bankowe

{

private:

char nazwisko[30];

char adres[50];

double stan_konta;

public:

konto_bankowe();

~konto_bankowe();

void wplata(double);

void wyplata(double);

void dopisz_procenty(double);

void zmiana_danych();

};

7.2.1. Przeciążanie

Język C++ zawiera dwa rodzaje przeciążania : przeciążanie funkcji i

przeciążanie operatora. Używając przeciążania funkcji, można korzystać z

więcej niż jednej wersji funkcji z tą samą nazwą. Stosując przeciążanie

operatora, można nadawać nowe znaczenie standardowym operatorem C++.

W przykładzie zdefiniowane poprzednio klasy konto_bankowe można

wprowadzić zmiany w celu dołączenia przeciążonej funkcji i przeciążonego

operatora.

class konto_bankowe

{

private:

char nazwisko[30];

char adres[50];

double stan_konta;

background image

public:

konto_bankowe();

~konto_bankowe();

void wplata(double);

void wyplata(double);

void dopisz_procenty(double);

void zmiana_danych();

void zmiana_danych(char wspolwlasciciel[30]);

bool operator-=(double debet);

};

W przykładzie tym funkcja zmiana_danych jest przeciążona. Zadeklarowane

zostały prototypy dwóch jej wersji. Odpowiednia wersja jest wybierana w

zależności od tego, czy w wywołaniu funkcji obecny jest argument. Operator -=

został przeciążony, tzn. słowo kluczowe operator oznajmia, że operator -= ma

nadane specjalne znaczenie, gdy używany jest łącznie z egzemplarzem klasy

konto_bankowe.operator.

7.2.2. Dziedziczenie

Dziedziczenie klas jest jedną z głównych właściwości programowania

obiektowego. W języku C++ jeżeli zdefiniowano już jedną klasę bazową, to

można także zadeklarować klasę pochodną, która przejmuje wszystkie atrybuty

klasy bazowej i dodaje swoje. Mówi się, że klasa pochodna dziedziczy z klasy

bazowej. Można budować hierarchię klas pochodnych o arbitralnej głębokości.

Opierając się na przykładzie przytaczanym wcześniej można zadeklarować klasę

konto_VISA, która będzie klasą pochodną klasy konto_bankowe. W tym celu

należy jednak zamienić słowo kluczowe private, które występowało w klasie

konto_bankowe na słowo kluczowe protected, aby funkcje składowe tek klasy

mogły być dziedziczone przez klasę pochodną konto_VISA.

background image

Klasa bazowa:

class konto_bankowe

{

protected:

char nazwisko[30];

char adres[50];

double stan_konta;

public:

konto_bankowe();

~konto_bankowe();

void wplata(double);

void wyplata(double);

void dopisz_procenty(double);

void zmiana_danych();

void zmiana_danych(char wspolwlasciciel[30]);

bool operator-=(double debet);

};

Klasa pochodna:

class konto_VISA : public konto_bankowe

{

private:

double numer_karty;

public:

void obsluga_karty();

void wplata(double);

void wyplata(double);

}

background image

Klasa konto_VISA dziedziczy wszystkie funkcje składowe klasy bazowej. Jeżeli

w tej klasie dziedziczone funkcje zostały ponownie zadeklarowane, mówimy o

tej ponownej deklaracji jako o przesłonięciu funkcji dziedziczonych (np. funkcje

wpłata i wypłata). Jednak dziedziczone funkcje nie muszą być przesłaniane.

Mogą one być zdeklarowane po raz pierwszy w klasie pochodnej i łączyć

dziedziczone dane i funkcje jako składowe tej klasy.

7.3. System wejścia/wyjścia w C++

Język C++ zawiera nową bibliotekę funkcji wejścia/wyjścia opartą na

deklaracjach zawartych w pliku nagłówkowym iostream.h. Deklaracje zawarte

w tym pliku przeciążają operatory przesunięcia >> oraz <<, nadając im

znaczenie operatorów wejścia i wyjścia. Można używać tych operatorów w

połączeniu z czterema standardowymi strumieniami wejścia i wyjścia:

cin

Standardowy strumień wejściowy

cout

Standardowy strumień wyjściowy

cerr

Standardowy strumień wyjściowy komunikatów o błędach

clog

Buforowany odpowiednik cerr, stosowany przy dużej ilości

danych wyjściowych

Standardowy strumień wejścia najczęściej reprezentuje klawiaturę, a

standardowy strumień wyjścia – ekran monitora. Przykładowo, dla

zdefiniowanych zmiennych:

char c; int i; float f; double d;

można wyświetlić ich wartości przez użycie instrukcji :

cout << c << i << f << d << ”\n”;

W podobny sposób można odczytać wartości ze strumienia wejściowego:

cin >> c >> i >> f >> d;

Dla wczytania z strumienia wejściowego jednego znaku i zapisaniu go w

zmiennej c należy wykorzystać funkcję składową get klasy istream:

background image

#include <iostream.h>

int main()

{

char c;

while (cin.get(c)) cout.put(c);

return(0);

}

Funkcja składowa put klasy ostream wstawia jeden znak do strumienia

wyjściowego. Dla „wyłowienia” ze strumienia wejściowego ciągu znaków i

zapisaniu ich w buforze można wykorzystać funkcję getline. Funkcja ta może

domyślnie kończy kopiowanie znaków po napotkaniu w strumieniu danych

znaku końca pliku EOF. Znak ten zdefiniowany jest w pliku iostream.h i

reprezentowany przez naciśnięte klawisze Ctrl+Z. Funkcja write wstawia

określoną parametrem ilość znaków do strumienia wyjściowego. Funkcja

gcount zwraca liczbę znaków wyłowionych przez ostatnie wywołanie getline.

Zasadę stosowania tych funkcji wyjaśnia poniższy program:

#include <iostream.h>

const int MAX=80;

int main()

{

char bufor[MAX];

while (cin.getline(bufor,MAX))

{

int ile_znakow;

ile_znakow=cin.gcount();

cout.write(bufor,ile_znakow);

}

return(0);

}

background image

II. Projektowanie i tworzenie programów w pakietach Borland C++ v.3.1

oraz Visual C++

1. Charakterystyka zintegrowanego środowiska programowania Borland C++

1.1. Elementy składowe pakietu Borland C++ v.3.1

Borland C++ v.3.1. jest pakietem umożliwiającym tworzenie oprogramowania

w języku C/C++ zawierającym wszystkie właściwości standardu języka C

wzbogacone o techniki programowania obiektowego. W skład pakietu wchodzą

następujące elementy:

zintegrowany system programowania, zawierający wszystkie narzędzia

niezbędne do tworzenia programów i usuwania usterek, wykorzystania

innych programów pakietu, w tym wersja systemu zintegrowanego

przeznaczona dla systemu MS WINDOWS,

zewnętrzny kompilator języka C/C++,

Turbo Asembler v 3.0 – asembler udostępniający programiście obiektowe

techniki programowania w języku wewnętrznym procesora,

Turbo Debugger v. 3.0 – program służący do umiejscawiania i usuwania

usterek w programach,

Turbo Profiler v.30 – program do badania czasów wykonywania

poszczególnych elementów programu,

wiele innych programów wspomagających tworzenie oprogramowania dla

sytemu DOS i Windows.

W kolejnych rozdziałach zostaną omówione te elementy pakietu, które są

niezbędne do tworzenia oprogramowania w języku C/C++: zintegrowany system

programowania BC.exe oraz kompilator zewnętrzny BCC.exe.

background image

1.2. Uruchomienie i wyjście z systemu zintegrowanego

Po zainstalowaniu pakietu Borland C++ v.3.1 w celu uruchomienia systemu

zintegrowanego należy wykonać polecenie o następującym formacie ogólnym:

BC [plik_źródłowy\nazwa_projektu]

gdzie plik_źródłowy powinien okreslać nazwę pliku tekstowego, który istnieje

lub ma zostać utworzony, nazwa_projektu oznacza projekt, który ma zostać

wczytany bezpośrednio po uruchomieniu systemu.

W celu zakończenia pracy z systemem zintegrowanym wykonywane jest

polecenie FILE/EXIT (kombinacja klawiszy ALT-X). Powoduje ono zapisanie

aktualnie przetwarzanego projektu i informacji o stanie systemu w zbiorach

konfiguracyjnych.

Po uruchomieniu systemu możliwe jest wykonywanie następujących czynności:

edycji zbiorów źródłowych, w wielu oknach edycyjnych, z możliwością

operacji edytorskich polegających na wycinaniu, kopiowaniu, powielaniu i

usuwaniu fragmentów tekstu,

kompilacji plików źródłowych,

generowanie plików źródłowych ze skompilowanych modułów,

wykonywanie i usuwanie usterek w programach w C/C++,

zarządzanie złożonymi programami za pomocą tzw. projektów,

wywoływanie innych programów pakietu.

Podane w wywołaniu systemu zintegrowanego pojęcie projektu określa listę

elementów składowych tworzonego programu:

moduły źródłowe programu (zbiory o rozszerzenicah: *.c, *.cpp),

moduły w postaci *.obj (już skompilowane),

biblioteki *.lib.

Program zarządzający projektami w systemie zintegrowanym rozpoznaje typy

zbiorów na podstawie ich rozszerzeń i w zależności od tego będą one

traktowane w różny sposób:

background image

moduły źródłowe programu (*.c, *.cpp) zostają skompilowane do zbiorów

*.obj, a uzyskane w ten sposób moduły, *.obj zostaną wykorzystane później

w czasie łączenia,

moduły *.obj i biblioteki zostaną wykorzystane dopiero w fazie łączenia.

1.3. Obsługa systemu zintegrowanego Borland C++ v. 3.1.

Po uruchomieniu systemu (program BC.exe) jego obsługa jest możliwa zarówno

za pomocą myszki jak i klawiatury. Ekran w systemie zintegrowanym jest

podzielony na trzy obszary (rys.1):

wiersz menu,

część roboczą ekranu,

wiersz statusu.

Rys.1 Podział ekranu w systemie zintegrowanym

background image

Wiersz menu (1 wiersz od góry ekranu) umożliwia dostęp do wszystkich

poleceń systemu. Przejście do wiersza menu umożliwia klawisz F10, natomiast

szybszą metodą wyboru konkretnej opcji jest naciśnięcie kombinacji klawisza

Alt i klawisza reprezentującego podświetlony znak wybieranej grupy opcji. Na

przykład naciśnięcie Alt-F spowoduje wybranie grupy opcji FILE.

Rezultatem wyboru opcji jest wyświetlenie okna zawierającego polecenia lub

kolejne opcje. Na rysunku nr 2 przedstawiono widok ekranu po wybraniu opcji

OPTION.

Rys.2. Menu w środowisku zintegrowanym

Wybór polecenia w okienku menu jest możliwy za pomocą klawiszy kursorów

oraz klawisza Enter lub za pośrednictwem wskazania kursorem myszki i

podwójnym szybkim ‘kliknięciem’.

Część robocza ekranu stanowi główny teren działań użytkownika środowiska,

gdyż w niej wyświetlane są okna tekstowe będące głównym miejscem pracy.

Wygląd typowego okna przedstawiono na rysunku nr 3.

background image

Rys.3. Okno w systemie zintegrowanym

Aktywne okno systemowe, w którym wykonywane są operacje wyróżnia

podwójna ramka oraz numer wyświetlany w lewej części ramki okna. Możliwe

jest otwarcie kilku lub nawet kilkunastu okienek operacyjnych. Zmiana okna

aktywnego jest dokonywana poprzez naciśniecie klawisza myszki przy

ustawieniu kursora myszki w obszarze okna nieaktywnego, przez użycie

polecenia Widow/Next lub naciśnięcie klawisza Alt-numer-okna.

Okna mogą być przesuwane, powiększane, zmniejszane i wyświetlane w

różnych układach. Wszystkie te operacje można wykonać albo przez wybranie

polecenia z grupy poleceń Window, albo za pomocą myszki:

zamknięcie okna – poprzez naciśnięcie klawisza myszki przy położeniu

kursora w obszarze piktogramu zamknięcia okna,

przesunięcie okna – poprzez naciśnięcie klawisza myszki, gdy jej kursor

znajduje się w obszarze krawędzi okna i trzymając go przesunięciu myszki,

background image

zmiana rozmiarów okna – poprzez naciśnięciu klawisza myszki, gdy jej

kursor znajduje się w obszarze piktogramu zmiany rozmiarów okna i po

przesunięciu myszki,

powiększenie lub powrót do poprzednich rozmiarów (Zoom) – poprzez

naciśnięcie klawisza myszki przy położeniu jej kursora w obszarze

piktogramu powiększania okna.

W systemie zintegrowanym Borland korzysta się z kilku rodzajów okien:

okna edycyjne,

okna komunikatów,

okna projektów,

okna obserwowanych wyrażeń,

okna dialogowe.

Na szczególną uwagę zasługują okna dialogowe, gdyż ich zastosowanie i

funkcjonowanie różnią je nieco od pozostałych. Służą one do wymiany

informacji pomiędzy użytkownikiem a systemem. Rozmiary okna dialogowego

są stałe, natomiast dopuszczalne jest jego przesuwanie. W oknach dialogowych

wyróżniane są następujące elementy (rysunek nr 4):

wiersze wprowadzania danych – pozwalają na wprowadzanie informacji z

klawiatury,

listy danych wprowadzonych w wierszach wprowadzania – po naciśnięciu

klawisza strzałka w dół w czasie wprowadzania danych zostanie wyświetlona

lista wprowadzonych wcześniej informacji. Z listy tej można wybrać i

przenieść daną do wiersza wprowadzania,

przyciski – służą do wykonywania różnych operacji. Trzy przyciski mają

charakter uniwersalny: OK – potwierdza ustalenia poczynione w oknie

dialogowym, Cancel – oznacza rezygnację z wprowadzonych w oknie

modyfikacji, Help – wywołuje podręczną pomoc dla danego okna

dialogowego.

background image

listy łańcuchów – umożliwiają wybór jednego z wyświetlanych w nich

elementów listy

Rys. 4. Elementy okna dialogowego

podokienka z opcjami – umożliwiające włączenie lub wyłączenie jednej lub

kilku opcji z danej grupy jednocześnie (rysunek nr 5).

Rys. 5. Podokienka z opcjami

Wiersz statusu służy do wyświetlania informacji pomocniczych dotyczących

aktualnie wybranego polecenia oraz aktualnie dostępnych klawiszach szybkiego

wyboru. Wyświetlenie polecenia mogą być wywoływane za pomocą klawiatury

lub myszki.

background image

1.4. Edycja zbiorów źródłowych

Kod źródłowy programów opracowywany jest w oknach edycyjnych, które

stanowią główny składnik części roboczej ekranu.


Wyszukiwarka

Podobne podstrony:
jezyk C skrypt cz 2
Medycyna Katastrof skrypt cz 1
Estetyka skrypt cz 2, Problem wartości
Prezenterzy sportowi i piękny język polski cz 2
Kopia skrypt cz. ogólna KK i KW, SZKOŁA POLICJI W PILE
Medycyna Katastrof skrypt cz 3
skrypt cz ogólna i prawo rodzinne, Prawo cywilne
Teoria bytu skrypt cz II
Io 5 Język UML, cz I
Język skryptowy Tcl i pakiet okienkowy Tk W Paluszyński
Medycyna Katastrof skrypt cz 2

więcej podobnych podstron