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ą, 

Ŝ

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ć 

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), 

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 

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Ŝenie1instrukcja1; 

case wyraŜenie2instrukcja; 

... 

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ów  –  case  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ę do – while (”wykonuj - dopóki” ) 

 

4.4.3.1. Instrukcja for 

Instrukcja cyklu for ma postać: 

For (wyraŜenie_inicjującewyraŜenie_warunkowewyraŜenie_zwiększające

 

instrukcja 

WyraŜenie_inicjujące,  wyraŜenie_warunkowe  i  wyraŜenie_zwiększające  są 

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_warunkoweinstrukcja 

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() 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() 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ć: 

classstruct 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.