background image

 
 
 
 
 
Programowanie Komputerów 

 
 
 
 
 
 
Skrypt wykładów  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Opracowali:  Jarosław S. Walijewski 

Zenon A. Sosnowski 

background image

ii

 

Literatura 
 


 

Skrypt wykładów: 

opal:// H:/C 
 



 

Brian W. Kernighan Dennis M. Ritchie 

 „ Język ANSI C ”

 

 WNT 
 



 

Claude Delannoy  

 

 „Ćwiczenia z języka C ”

 

WNT 
 



 

Marek Kotowski 

„Wysokie C ”

 

Wydawnictwo Lupus 
 



 

Jan Bielecki  

„Encyklopedia Języka C dla IBM PC”

 

WNT 
 



 

Marc J. Rochind 

„Programowanie w systemie UNIX dla zaawansowanych”

 

WNT 
 
 
 

 
 
 

background image

iii

 

Program wykładów 

1. 

H

ISTORIA

............................................................................................ 1 

2. 

P

RZEGLĄD KONSTRUKCJI JĘZYKOWYCH

......................................... 3 

3. 

B

UDOWA PROGRAMU

....................................................................... 16 

4. 

P

ODSTAWOWE TYPY DANYCH

......................................................... 18 

5. 

O

PERATORY

..................................................................................... 23 

6. 

I

NSTRUKCJE

..................................................................................... 27 

7. 

F

UNKCJE

........................................................................................... 31 

8. 

T

ABLICE 

1-

WYMIAROWE

................................................................. 40 

9. 

T

ABLICE WIELOWYMIAROWE

......................................................... 48 

10.  A

RGUMENTY MAIN

........................................................................... 52 

11.  D

EFINIOWANIE TYPÓW

.................................................................... 53 

12.  S

TRUKTURY

...................................................................................... 55 

13.  U

NIE

.................................................................................................. 62 

14.  M

ODELE PAMIECI

............................................................................ 65 

15.  D

YNAMICZNY PRZYDZIAŁ PAMIĘCI

................................................ 68 

16.  S

TRUKTURY LISTOWE

...................................................................... 70 

17.  S

TOS

.................................................................................................. 81 

18.  D

RZEWA BINARNE

............................................................................ 87 

19.  P

REPROCESOR

.................................................................................. 92 

20.  F

UNKCJE ZE ZMIENNĄ ILOŚCIĄ ARGUMENTÓW

........................... 105 

21.  W

EJŚCIE

/

WYJŚCIE

......................................................................... 110 

22.  C

 W SYSTEMIE 

UNIX ..................................................................... 122 

23.  W

YSOKIE 

C .............................B

ŁĄD

!

 

N

IE ZDEFINIOWANO ZAKŁADKI

. 

background image

 1 

1.

 

Historia 

 

1967    BCPL 
 
 

Martin Richards 

 

University, Cambridge, MA, USA 

 

Język beztypowy,  

 

słowo maszynowe, wskaźniki,  

 

arytmetyka wskaźników 

 

 

 

Bell Laboratories, New Jersey 

 
1969    B   

 Ken Thompson 

     pierwsza wersja systemu UNIX 
     j. wewnętrzny + B      1970 rok 
 
1972    C   

 Dennis Ritchie 

     pierwsza implementacja na PDP-11 
    Język systemowy systemu UNIX  (90% kodu) 
 
1978    The C Programming Language 
     klasyczna definicja Języka 
 
1983    American National Standard Institute 
        ANSI C 
 
koniec lat 80   
 

standard Języka, usuwa niejednoznaczność i 

 

 wprowadza rozszerzenie 

 

 

 

C++ 

 
 
 

background image

 2 

1.1.

 

 

Dlaczego  C? 

 

język programowania, podstawowy język programowania.  
 
Język programowania strukturalnego, ogólnego przeznaczenia. Pozwala  
zbliżyć się do sprzętu i programowania operacji, które zwykle wyzmagają 
j.maszynowowego. 
 
C jest mały -  
 

mniej słów kluczowych niż w Pascalu przy większych 

możliwościach obliczeniowych. 

 
C silnie związany z UNIX 
 

nie potrzeba wbudowywać w programy kontroli we/wyj  

lub 

obslugi przerwań 

 

 w C używa się procedur bibliotecznych SO 

 
C - przenaszalny 
 

preprocesor (różne implementacje) 

 
C - zwięzły 
 

dużo operatorów 

(dostęp do słowa, arytmetka adresowa) 

 
C - modułowy 
 

jeden rodzaj procedur - funkcje zewnętrzne z parametrami 

przekazywanymi przez wartość 
 

1.1.1.

 

Wady 



 

skomplikowana składnia 



 

brak automatycznej kontroli indeksów tablic 



 

wielokrotne użycie operatorów +, = 



 

=     operator przypisania  



 

= =    operator równości 

background image

 3 

2.

 

Przegląd konstrukcji językowych 

2.1.

 

 

Wyjście 

 
#include <stdio.h> 
main () 

  printf("Programowanie w C nie jest trudne!\n"); 

 
 
printf ("Programowanie"); 
printf (" w C nie jest"); 
printf (" trudne\n"); 
 
 

Zmienne, wyrażenia, przypisanie wartości 

 
/*Temperatura Fahrenheita-Celsjusza*/ 
#include <stdio.h> 
main ( ) 

 int fahr; 
 float cel; 
 cel = (5.0/9.0)*(fahr-32); 
 printf( 
 "%d stopni Fahrenheita to %f stopni Celsjusza \n", 
         fahr,cel); 

 
 

background image

 4 

2.2.

 

 

Preprocesor  

 
#define PI  3.141592 
#define C   299792.458 
    

 

/* pr

ę

dko

ść

 

ś

wiatła w km/sec */ 

 
 
#include  <    .h>   - standardowa 
#include  " .h" - zdefiniowanie przez u

ż

ytkownika 

 

Plik  ocean.h 

   #include <stdio.h> 
   #define OBSZAR              2337 
   #define MIL_KW_NA_KM_KW     0.3861021585424458 
   #define STOP_KW_NA_MIL_KW    (5280 + 5280) 
   #define CALI_KW_NA_STOPE_KW  144 
 

Plik ocean.c 

   /*Mierzenie Pacyfiku*/ 
 #include "ocean.h" 
 main ( ) 
  { 
   const int ocean = OBSZAR; /* w km kwadratowych*/ 
   double mil_km,stop_kw,cal_kw; 
   mil_kw  = MIL_KW_NA_KM_KW * ocean; 
   stop_kw = STOP_KW_NA_MIL_KW*mil_kw; 
   cal_kw  = CAL_KW_NA_STOP_KW*stop_kw; 
   printf( 
   "Obszar Pacyfiku wynosi %d km kwadratowych \n", 
           obszar); 
   printf("W innych jednostkach \n"); 
   printf("%22.7e mil kwadratowych \n", mil_kw); 
 
   .   .   . 
 
  } 

background image

 5 

2.3.

 

Funkcje wejścia i wyjścia

 

   printf(const char* format,...) 
 
c          

znak 

d          

liczba calkowita 

e          

liczba zmiennoprzecinkowa 

f          

liczba zmiennoprzecinkowa 

g          

liczba zmiennoprzecinkowa 

s          

napis 

 
   printf("abc"); 
   printf("%s","abc"); 
   printf("%c%c%c",'a','b','c'); 
   printf("%c%3c%5c",'A','B','C') 

   A  B    C 
 
Wprowadzanie danych    

scanf(constchar* format,...) 

 

 
    scanf("%d",&x); 
               & - operator adresowy 
                  adres x 
Kody konwersji: 

lf     - 

  double 

Lf    - 

  long double 

 
 #include <stdio.h> 
 main ( ) 
  { 
   char c1,c2,c3; 
   int i;   float x;    double y; 
   printf("\n%s\n%s%s", 
         "Wprowad

ź

 trzy znaki", 

         "liczb

ę

 całkowit

ą

, zmiennoprzecinkow

ą

", 

         " i liczb

ę

 podwójnej  precyzji"); 

scanf("%c%c%c%d%f%lf",&c1,&c2,&c3,&i,&x,&y); 
 printf("\n oto dane, które wprowadziłes \n");   
printf("%3c%3c%3c%5d%17e%17e\n\n",c1,c2,c3,i,x,y); 
  } 

background image

 6 

2.4.

 

 

Sterowanie 

 

if (wyrażenie) 

instrukcja 

 
 
   a=1; 
   if (b==3) 
        a=5; 
   printf("%d",a); 
 
   if(a==3) 
     { 
      b=5; 
      c=7; 
     } 
 
 

   if (wyrażenie) 

           instrukcja1 

 

 

 

 

else 

           instrukcja2 

 
 
   if(l==0) 
     { 
      a=2; 
      b=3; 
     } 
  else 
     { 
      a=-2; 
      b=-3; 
     } 
 
 

background image

 7 

2.5.

 

Instrukcja pętli  

 

while (wyrażenie) 

 

 

 instrukcja 

 
#include <stdio.h> 
main ( ) 

  int i=1, sum=0; 
   while  (i<=5) 
     { 
       sum+=i; 
       ++i; 
     } 
 printf("suma=%d", sum); 

 

sum+=i;        sum = sum+i; 
 
 
     zmienna op= wyrażenie 
     zmienna = zmienna op wyrażenie 
gdzie 
 

op    +    -    *   / 

 
 
++i;          i=i+1;        - -i;     i=i-1; 
 
++zm   przed wyrażeniem 
zm++   po wyrażeniu 

 
h=5; 
x=h++;   5 
x=++h;   6 
 

background image

 8 

2.5.1.

 

Pętla for 

for (wyrażenie1;wyrażenie2;wyrażenie3) 

    instrukcja 

   wyrażenie1; 
   while (wyra
żenie2) 
   { 
   instrukcja; 
   wyra
żenie3 
   } 

 
for (i=1;i<=5;++i) 
   sum+=i; 
 
 
/*obliczanie elementu minimalnego, maksymalnego 
 

i sredniej arytmentycznej */ 

#include <stdio.h> 
#include <stdlib.h> 
main ( ) 
  { 
   int i; 
   double x, min,max,sr,suma; 
    if(scanf("%lf",&x) != 1) 
     { printf("Brak danych \n"); exit (1);} 
    min=max=suma=sr=x; 
    printf("Nr   Min   Max   Suma   Sr  \n"); 
    printf("%d %f %f %f %f %f" , 
            1, x, min, max, suma, sr); 
   for (i=2;scanf("%lf",&x)==1;++i) 
     { 
       if(x<min)      min=x; 
       else if (x>max)  max=x; 
      suma+=x; 
      sr=suma/i; 
      printf("%d %f %f %f %f %f\n", 
               i, x, min, max, suma, sr) 
     } 

background image

 9 

2.6.

 

 

Funkcje 

 

   double potega (double x, double y) 
  
  prototyp funkcji 
   double pot
ęga (double, double); 

 
 
  #include <stdio.h> 
  main ( ) 
    { 
     int i,h; 
     float min, max, x; 
     float minimum  
         (float, float), maximum (float, float); 
     void prt_inf (void); 
  
     prt_inf( ); 
     printf("Wprowad

ź

 n:"); 

     scanf("%d,&n); 
printf("\n Wprowad

ź

 %d liczb rzeczywistych :", n); 

     scanf("%f",&x); 
     min=max=x; 
     for(i=2; i<=n; ++i) 
       { 
        scanf("%f",&x); 
        min = minimum (min,x); 
        max = maximum (max,x); 
       } 
     printf( 
 "Warto

ść

 minimalna: %f\nWarto

ść

 maxymalna: %f\n",  

         min, max); 
     }   
void prt_inf (void) 
     { 
      printf("\n\n %s \n%s \n", 
       "Program wczytuje długo

ść

 ci

ą

gu" 

       ............................. 
      } 
  

background image

 10 

float minimum (float x, float y) 
        { 
         if (x<y) 
               return x; 
            else 
               return y; 
        } 
       
float maximum (float x, float y) 
        { 
         if (x>y) 
               return x; 
            else 
               return y; 
        } 
 

2.6.1.

 

Przekazywanie parametrów "przez wartość" 

 
     #include <stdio.h> 
      main ( ) 
      { 
       int a=1; 
       void proba_zamiany(int); 
 
       printf("%d \n",a);         
 

 

 

 

 

 

/* drukuje si

ę

 1 */ 

       proba_zamiany (a); 
       printf("%d \n",a);  
 

 

 

 

/*jeszcze raz drukuj si

ę

 1 */  

 

 

 
      void proba_zamiany (int a) 
      { 
       a=2; 
      } 

background image

 11 

2.7.

 

Tablice 

int t[3]; 
 
 

t[0], t[1], t[2] 

 
Numeracja indeksów tablic zaczyna się od zera. 
Przykład.

 

/*********************************************** 
Program wczytuje 5 warotosci, porzadkuje je,      
 oblicza sume i wartosc srednia         

 

   

 **********************************************/ 
 #include   <stdio.h> 
 #define 

WYMIAR 5 

 main() 
   { 
     int i, j, tab[WYMIAR], sum = 0, rob; 
     printf("\n\nWprowadz %d wartosci: ", WYMIAR); 
     for (i = 0; i < WYMIAR; ++i) 
       { 
 

 

scanf("%d", &tab[i]); 

 

 

sum += tab[i]; 

       } 
     for (i = 0; i < WYMIAR - 1; ++i)  
 

 

 

/* Sortowanie babelkowe */ 

       for (j = WYMIAR - 1; j > i; --j) 
 

  if (tab[j-1] < tab[j]) 

        

 

 

 

/* Sprawdzenie porzadku */ 

 

    { 

 

     rob = tab[j-1]; 

 

     tab[j-1] = tab[j]; 

 

     tab[j] = rob; 

 

    } 

     printf("\nUporzadkowane wartosci:\n"); 
     for (i = 0; i < WYMIAR; ++i) 
       printf("     tab[%d] = %5d\n", i, tab[i]); 
     printf("\n\n%s%d\n%s%f\n", "Suma = ", sum, 
 

 

"Srednia arytmetyczna = ", 

 

 

(double) sum / WYMIAR); 

  } 

background image

 12 

2.8.

 

Napisy 

 

Napis jest tablicą znaków. 

 
Funkcje standardowe (w <stdio.h>). 
 

Czytanie znaku z klawiatury - getchar(); 

 

Wyprowadzenie znaku na ekran - putchar(); 

 

Przykład. 

 
#include 

<stdio.h> 

#include 

<ctype.h> 

#define 

 

MAX_DL  100 

main() 
  { 
   char c, nazwa[MAX_DL]; 
   int i, sum = 0; 
   printf("\n\nNapisz swoje imie: "); 
   for (i = 0; (c = getchar()) != '\n'; ++i) 
     { 
      nazwa[i] = c; 
      if (isalpha(c)) 
 

sum += c; 

     } 
   nazwa[i] = '\0'; 
  printf("\n%s%s%s\n", "Witaj ", nazwa, " !"); 

  printf("Suma kodow liter Twojego imienia daje %d.\n"   
 

 

 

,sum); 

   printf("\n%s\n", "Zgadnii co to jest:"); 
   for (--i; i >= 0; --i) 
     putchar(nazwa[i]); 
  } 

Komentarz: 

(c = getchar()) != '\n' 
isalpha();   <ctype.h> 
nazwa[i] = '\0'; 
\0 - null  - koniec napisu 

0  1  2  3  4  5  6  7  8  9  ... 
B a r b a r a \0
 

background image

 13 

2.9.

 

Wskaźniki 

 

Wskaźnik jest  adresem objektu w pamęci. 

Przykład. 

/********************************************/ 
/* Nazwa tablicy jest wska

ź

nikiem.          */ 

/********************************************/ 
 
#include 

<stdio.h> 

#include 

<string.h> 

#define 

 

MAX_DL  100 

main() 
  { 
   char c = 'a', *w, s[MAX_DL]; 
   w = &c; 
   printf("%c%c%c ", *w, *w + 1, *w + 2); 
   strcpy(s,"ABC"); 
  printf("%s %c%c%s\n", s, *s + 6, *s + 7, s + 1); 
strcpy(s,"program pokazuje przeksztalcanienapisow"); 
   w = s + 8; 
   for ( ; *w != '\0'; ++w) 
     { 
      if (*w == 'p') 
 

    *w = 'P'; 

      if (*w == ' ') 
 

    *w = '\n'; 

     } 
   printf("%s\n",s); 
  } 

Komentarze: 
 
Wynik działania programu: 
 
abc ABC GHBC 
program Pokazuje 
Przeksztalcanie 
naPisow 

 
   w = &c; 
 

background image

 14 

 
   printf("%c%c%c ", *w, *w + 1, *w + 2); 
 
*w    wartosc  'a' 
*w+1           'b' 
*w+2           'c' 
 
strcpy(s,"ABC");  kopiowanie napisow <string.h> 
 
0   1  2  3  4  . . . 
A   B  C \0 
 
 
 printf("%s %c%c%s\n", s, *s + 6, *s + 7, s + 1); 
 

s       wskaźnik do s[0]  (adres bazowy tablicy)     ABC 

 

*s      wartośc     s[0]   czyli 'A'    
 

 

a wieęc   *s+6    daje 'G' 

                    *s+7         'H' 

 

s+1     arytmetyka na wskaźnikach 
    wartoscią jest wskaźnik do s[1]               BC 
 

       
 
   w = s + 8 

        równoważne    

w = &s[8];

 

 

w = s  poprawne bo  w zmienna wskaźnikowa 

     s = w  źle ,    bo s stała wskaźnikowa 
 
 

s[i]

  równoważne 

*(s + i)

 

w[i]             *(w + i) 

 

++w

    poprawne 

++s

    błędne 

 

background image

 15 

2.10.

 

Pliki

 

#include 

<stdio.h> 

main() 
  { 
   FILE *wp; 
 
   wp = fopen("DANE.DAT","r"); 
   . . . 

 
 

Uwagi: 
1. FILE - typ standardowy zdefiniowany w <stdio.h> 
2. fopen(nazwa_pliku, tryb) - funkcja standardow <stdio.h> 
 

tryb: "r" - czytanie 

 

 

 "w" - pisanie   

 

 

 

 "a" - dołączanie 

 
 

Wartość funkcji NULL - pliku nie można otworzyć 

3. Odwołania do pliku realizuje się za pomocą zmiennej typu wskaźnik do 
FILE. 
4. Wszystkie pliki są automatyczne zamykane po zakończeniu programu. 
5. fclose() - zamknięcie pliku 
 
Przykład. 

#include  

<stdio.h> 

main() 
  { 
   int c, i, litery[26]; 
   FILE *wpd, *wpw; 
   wpd = fopen("DANE", "r"); 
   wpw = fopen("WYNIKI","w"); 
   for (i = 0; i < 26; ++i) 
     litery[i] = 0; 
   while ((c = getc(wpd)) != EOF) 
     if (c >= 'A' && c <= 'Z') 
       ++litery[c - 'A']; 
   for (i = 0; i < 26; ++i) 
     fprintf(wpw, "%c: %5d\n", 'A' + i, letter[i]); 
   putc('\n', wpw); 
  } 

background image

 16 

3.

 

Budowa programu 

 

Program w języku C jest ciągiem znaków. 

 

Alfabet: 
 

małe litery   

a b c ... z 

 

duże litery   

A B C ... Z 

 

cyfry 

 

 

0 1 2 ... 9 

 

znaki specjalne  + - * / = ( ) [ ] { } < > ' " 

                         ! @ # $ % & _ | ^ ~ \ . , ; : ?   
     spacja (odstęp), koniec linii, znak tabulacji 
 
Proces kompilacji: 
Tekst programu w języku C  ----> grupowanie znaków w atomy 
 

 

 

 

 

  ----> tłumaczenie na kod wynikowy 

 
Atomy ANSI C: 
 

 

 

słowa kluczowe 

 

 

 

identyfikatory 

 

 

 

stałe 

 

 

 

napisy 

 

 

 

operatory 

 

 

 

separatory 

 
Reguły syntaktyczne (gramatyka języka) 
 
 

Metajęzyk  - BNF (Backus-Naur Form) 

 
Produkcja: 
 

cyfra ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

 
 

Kategoria syntaktyczna cyfra jest równa z definicji 

0, lub 1, lub 2, ... , lub 9.  
 
Symbole używane w produkcjach: 
 

italics 

kategoria syntaktyczna 

 

::= 

 

"równe z definicji" 

 

 

lub 

 

{ }

1

 

 

wybór 1 elementu z wymienionych 

background image

 17 

 

{ }

0+

 

powtórzenie 0 lub więcej razy 

 

{ }

1+

 

powtórzenie 1 lub więcej razy 

 

{ }

op

 

opcjonalne 

 
Inne symbole są symbolami końcowymi języka. 
 
Przykłady: 
 

litera_lub_cyfra ::= litera | cyfra 

 

litera ::= mała_litera | duża_litera 

 

mała_litera ::= a | b | c | . . . | z 

 

duża_litera ::= A | B | C | . . . | Z  

 

 

 

ciąg_liter_lub_cyfr ::= { litera_lub_cyfra }

0+ 

 

    instrukcja_warunkowa ::= if (wyrażenieinstrukcja 
 

 

 

 

 

 

 

{ else instrukcja }

op

 

  
 
Słowa kluczowe: 
auto  

do   

goto   

signed 

 

unsigned 

break 

double 

if 

 

sizeof 

 

void 

case  

else  

int 

 

static  

volatile 

char  

enum 

 

long   

struc  

while 

const 

extern 

register 

switch 

continue 

float 

return 

typedef 

default 

for   

short union 

background image

 18 

4.

 

Podstawowe typy danych 

 
Deklaracja 
 

deklaracja ::= typ identyfikator { , identyfikator }

0+

 

 


 

Podstawowe typy danych (pełny zapis): 

 
char 

 

signed char 

 

unsigned char 

signed short int   

signed int  

 

signed long int 

unsigned short int  unsigned int 

 

unsigned long int 

float 

 

double 

 

 

long double 

 


 

Podstawowe typy danych: 

 
char 

 

 

signed char 

 

unsigned char 

short 

 

 

int   

 

 

long 

unsigned short   

 

unsigned    

 

uunsigned long 

float 

 

 

double 

 

 

long double 

4.1.

 

TYPY całkowite 

 
 

 

 

 

 

( Reprezentacja w Borland C++ ) 

 
TYP char 
 
typ   

 

 

ilość bitów 

 

zakres 

--------------------------------------------------------------------------------------- 
unsigned char   

 

 

 

0 do 255 

char, signed char 

 

 

 

-128 do 127 

 

 

 
 
Przykład: 

 

char c = 'a'; 

 

printf("%c",c);  

/* drukuje sie a */ 

 

printf("%d",c); /* drukuje si

ę

 97 */ 

 
 
 

background image

 19 

typ   

 

 

 

ilość bitów 

 

zakres 

---------------------------------------------------------------------------------- 
short, int 

 

 

 

16 

 

 

-32768 do 32767 

unsigned short, unsigned        16 

 

 

0 do 65535 

long 

 

 

 

32 

 

 

-2,147,483,648 do 

 

 

 

 

 

 

 

 

 2,147,483,647 

unsigned long 

 

 

 

32 

 

 

0 do 4,294,967,295 

 
 
Przykład: 

#define  

BIG 

2000000000 

main() 

 int a, b = BIG, c = BIG; 
 a = b + c;  

 

?* przekroczenie zakresu */ 


 

Przykłady stałych: 
 

32000  

stała typu int 

 

320000 

stała typu long int 

 
 
 
Kwalifikator 

 

 

typ   

 

stała 

----------------------------------------------------------------------- 
u lub U 

 

 

unsigned   

 

37U 

l lub L 

 

 

long                   

37L 

ul lub UL 

 

 

unsigned long   

37UL 

 
 

background image

 20 

4.2.

 

Typy zmiennoprzecinkowe 

 
typ   

ilość bitów 

 

zakres  

 

 

precyzja 

--------------------------------------------------------------------------------------- 
float 

32   

3.4*10^-38 do 3.4*10^38   

 

7 cyfr  

double 

64   

1.7*10^-308 do 1.7*10^308 

 

15 cyfr 

long double 

80   

3.4*10^-4932 do 1.1*10^4932   

19 cyfr 

 
 
Stała zmiennoprzecinkowa 
    - liczba zawieraj
ąca kropkę lub literę e lub E. 
 
Przykład: 
12.345 

3.   0.53   

2.34e-4 

2.5E3 

 
Uwaga: 
 

stała zmiennoprzecinkowa jest typu double 

 
Kwalifikator 

 

 

typ   

 

stała 

-------------------------------------------------------------------------- 
f lub F 

 

 

float  

 

 

3.7F 

l lub L 

 

 

long double 

 

3.7L 

Operator sizeof 
 
 

sizeof(obiekt) - ilość bajtów zajmowanych przez obiekt 

 
Przykład: 
 

sizeof(char) = 1 

 
 
FUNKCJE MATEMATYCZNE 

<math.h> 

 
sqrt() 

pow() 

exp() log() sin() cos() tan() 

background image

 21 

Konwrsja typów 

 
Ka
żde wyrażenie arytmetyczne nma wartość i typ 
 
Automatyczna konwersja typów wyra
żenia 
 

x op y 

 
1. Typy char, short, signed char, unsigned char s
ą zamieniane na typ 
int. 
 
2. Jezeli typy nadal nie s
ą zgodne to typ niższy zamienia się na typ 
wy
ższy zgodnie z hierarchi_ typów: 
int < unsigned < long < unsigned long < float < double < long double 
 
Przykład: 
 

char c;  

double d; 

float f; 

int i;  

 

long l; 

short s;  unsigned u; 

 
c - s / i 

 

- int 

u * 2.0 - i   

- double 

c + 3 

 

- int 

c + 5.0 

 

- double 

u * 7 - 1 

 

- unsigned 

7 * s * l  

- long 

d + s 

 

- double 

 

4.2.1.

 

Rzutowanie 

 
Wymuszenie konwersji typów: 
 

(typ) wyrażenie 

 
Przykład: 
 

(double) i 

 

(long) ('A' + 1.0) 

 

x = (float) ((int) y + 1) 

 

(double) (x = 77) 

 

(float) i + 3 

 

background image

 22 

4.3.

 

Stałe szesnastkowe i ósemkowe 

 
Cyfra szesnastkowa   

wartość dziesi_tna 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. . . 

 

 

 

 

 

 

 

 

 

 

10 

 

 

 

 

 

11 

 

 

 

 

 

12 

 

 

 

 

 

13 

 

 

 

 

 

14 

 

 

 

 

 

15 

 
 

h

n

h

n-1

 . . .h

1

h

  
 

h

n

*16

n

 + h

n-1

*16

n-1

 + . . .+ h

1

*16 + h

0

 

 
0xA0F3C 

 

 

688260 

0x2A 

 

 

 

2*16 + 10 = 42 

0xB3 

 

 

 

B*16 + 3 = 11*16 + 3 = 179 

0x113 

 

 

1*16

2

 + 1*16 + 3 = 275 

 
 

background image

 23 

5.

 

Operatory  

Priorytety operatorów: 
------------------------ 
1. ()  [] 

 

 

 

 

 

 

 

 

 

LP 

2. ++ ~ 

--  ! 

sizeof 

(typ)  

 

jedn.arg.(*, -) 

*(wsk) 

&(adres)   

 

 

 

PL 

3.  * 

 

 

 

 

 

 

 

LP 

4.  +  

 

 

 

 

 

 

 

 

LP 

5.  << 

>>   

 

 

 

 

 

 

 

LP 

6.  < 

<=  > 

>=   

 

 

 

 

 

LP 

7.  == 

!=   

 

 

 

 

 

 

 

LP 

8.   & 

 

 

 

 

 

 

 

 

 

LP 

9.  ^ 

 

 

 

 

 

 

 

 

 

LP 

10.  | 

 

 

 

 

 

 

 

 

 

LP 

11.  && 

 

 

 

 

 

 

 

 

 

LP 

12.  || 

 

 

 

 

 

 

 

 

 

LP 

13.  ?: 

 

 

 

 

 

 

 

 

 

PL 

14.  = 

+=  -= 

/=   itp. 

 

 

 

 

 

PL 

15   , (operator przecinkowy) 

 

 

 

 

 

 

LP 

 

5.1.

 

Operatory relacji, operatory logiczne 

 
1-arg (+ i -)  ++  --  !   

 

p -> l 

*   /  % 

 

 

 

 

 

l -> p 

+  -   

 

 

 

 

 

l -> p 

<  <=  >  >= 

 

 

 

 

l -> p 

==  != 

 

 

 

 

 

l -> p 

&&  (and) 

 

 

 

 

 

l -> p 

|| 

(or) 

 

 

 

 

 

l -> p 

=  +=  -=  *=  /=   

 

 

p -> l 

 
 
 
Przykład: 

char c = 'w'; 
int i = 1, j = 2, k = -7; 
double x = 7e+33, y = 0.0001; 
 

background image

 24 

'a' + 1 < c

   

 

 

 

-i - 5*j >= k+1

  

 

 

((-i) - (5*j)) >= (k+1) 

3 < j < 5

 

 

 

 

 

j = 4

 

 

 

 

 

j = 7

 

 

 

 

 

 
Przykład. 

char c = 'A'; 
int i = 7, j = 7; 
double x = 0.0, y = 2.5; 
 

!c

   

 

 

 

 

!(i - j)

 

 

 

 

 

!i - j 

 

 

 

 

-7 

!!(x+y) 

 

 

 

 

5.2.

 

Operatory zwiekszanie i zmmiejszania 

++i;          i=i+1;         - -i;     i=i-1; 
 
++zm     

- -zm 

 

przed wyrażeniem 

     zm++        

    zm- -   

po wyrażeniu 

 
h=5; 
x=h++;   5 
x=++h;   6 

 

5.3.

 

Operatory Bitowe 

 
Logiczne: 
 

~ - negacja 

 

& - koniunkcja 

 

^ - różnica symetryczna 

 

| - alternatywa 

 
Przesunięcie: 
 

>> - w prawo 

 

<< - w lewo 

 

background image

 25 

 
  
 
 
 
 
 

5.3.1.

 

Uzupełnienie jedynkowe 

 
long 

a = 70707; 

 
 

00000000 00000001 00010100 00110011 

 

~a 

 

11111111 11111110 11101011 11001100 

 

5.3.2.

 

Bitowe operatory logiczne 

 
 

a&b  a^b  a|b 

 

------------------------ 

 

 

1  

 

 

 
long 

a = 33333, b = -77777; 

 

00000000 00000000 10000010 00110101 

11111111 11111110 11010000 00101111 

a&b 00000000 00000000 10000000 00100000 
a|b  11111111 11111110 11010010 00111111 
a^b  11111111 11111110 01010010 00011010 
 

5.3.3.

 

Operatory przesuwania 

 

wyr1 op wyr2 

 
char c = 'Z'; 
 

 

01011010 

background image

 26 

c << 1 

10110100 

cc>>1 

01011010 

 
x << y       mnożenie  x*2

x >> y       dzielenie   x / 2

 

 
 

background image

 27 

6.

 

Instrukcje 

6.1.

 

Instrukcja złożona 

 
instrukcja_zło
żona ::= { { deklaracje }

0+

 

 

 

 

 

 

{instrukcje }

0+

 } 

 

 

int a; 

 
 

a = b; 

  

. . . 


 

6.2.

 

Instrukcja pusta 

 

6.3.

 

Instrukcja warunkowa 

 

if ( wyrażenie1 ) 

 

if ( wyrażenie2 )   

 

 

 

 

 

instrukcja1 

 

else  

 

 

 

instrukcja2   

 

 

 

 
else wi
ąże się z najbliższym if   

 

 

 

6.4.

 

Instrukcje iteracyjne 

 

while ( wyrażenie ) instrukcja 

 

for (wyr1; wyr2; wyr3) 

 

instrukcja 

 

do instrukcja while ( wyrażenie ) 

 

do 
  { 

background image

 28 

   sum +=i; 
   scanf("%d", &i); 
   }  while (i > 0); 

6.5.

 

Instrukcja Skoku 

goto etykieta 

etykieta ::= identyfikator: 
Przykład 

goto dalej: 
koniec : exit(1); 
dalej: 
while(1) 

if(i++>10) goto koniec; 
printf("%3d",i); 

6.6.

 

Instrukcje strowania petlą 

break  

-    przerwanie działania pętli 

continue 

-   kontynuacja od początku pętli 

while(1) 

 

if(i++>10) break; 

 

printf("%3d",i); 


void main() 
{ int x,y; 
for(;;)  { 
   printf("Podaj dwie liczby (0 0 by zakonczyc)"); 
   scanf("%d%d",&x,&y); 
   if(!x&&!y)break; 
   printf("%3d + %3d = %6d\n",x,y,x+y); 
   printf("%3d - %3d = %6d\n",x,y,x-y); 
   printf("%3d * %3d = %6d\n",x,y,x*y); 
   if(!y)continue; 
   printf("%3d / %3d = %6d\n",x,y,x/y); 
 } 

void main() 
{ int x,y,c;   char s[500]="",*w; 
 w=s; 

background image

 29 

for(x=0;x<10;x++) 
  for(y=0;y<50;y++) { 
  c=getchar();   if(c=='\n') break;  
  *w++=c; 
 } 
 w='\0';  printf(s); 

6.7.

 

Instrukcja  wyboru 

instrukcja _switch   ::= switch(wyrażenie_skoku)  
 istrukcja_wyboru | instrukcja_domy
ślna |  blok_przełączeniowy
 

 

instrukcja_wyboru  ::= / case stała_składowa : /1+  instrukcje 
instrukcja_domyslna 

::= default: instrukcje 

blok_przełączeniowy::= { /grupa_wyboru /0+ 
 

       /grupa_domyślna /opt 

 

grupa_wyboru  ::= / case stała_składowa : /1+   

/instrukcje/1+  

Grupa_domyślna  ::= default:  /instrukcje/1+ 
**************************************** 

switch(x) 
{ case 0:break; 
  case 2:printf("bardzo "); 
  case 1:printf("powazny "); 
  default:printf("blad\n"); 

************************************** 
switch(c) 

        case '+':w=a+b;break; 
        case '-':w=a-b;break; 
        case '*':w=a*b;break; 
        case '/':if(b) {e=1;break;} 
                 w=a/b;break; 
       default:e=2; ; 

 switch(e){ 
 case 0: printf("%d %c %d = %d\n",a,c,b,w);break; 
 case 2: printf(" Blad operatora");break; 
 case 1: printf( 

background image

 30 

 

 

" Blad dzialenia przez zero");break; 

 default: printf(" Nieznany blad ");break; 

6.8.

 

Operator przecinkowy 

 

wyrażenie_przecinkowe ::= wyrażenie , 

wyrażenie 

 
Uwagi: 
1. najni
ższy priorytet 
2. od lewej do prawej 
3. warto
ścią jest ostatnie wyrażenie 
 

or (sum = 0, i = 1; i <= n; sum += i, ++i) 
 

 

 
Przykłady: 

int i,j,k = 3; 
double x = 3.3; 
 
i = 1, j = 2, ++k+1 
 

(((i=1),(j=2)),((++k)+1) 

 

 

 
k != 1, ++x*2.0+1 
 

((k != 1), (((++x)*2.0)+1)  

9.6 

 

background image

 31 

7.

 

Funkcje 

 
Funkcja jako realizacja dekompozycyjnej metody rozwi
ązywania 
problemów zgodnie z Regułami programowania strukturalnego. 
 
Program w j
ęzyku C składa się z jednego lub więcej plików.  
Ka
żdy z nich może zawierać zero lub więcej funkcji.  
Jedn
ą z tych funkcji jest funkcja main. 
Funkcje nie mog
ą być zagnieżdżane. 
Działanie programu zaczyna si
ę od funkcji main. 
 
 

7.1.

 

Definicja funkcji 

 

 

typ nazwa_funkcji(listałparametrów) 

 

  { 

        deklaracje 
 

   instrukcje 

 

  } 

 

Przykłady: 

 

int potega(int n) 

 

  { 

        int i, p = 1; 
 
 

   for (i = 2; i <= n; ++i) 

 

     p *= i; 

        return p; 
 

  } 

 

void nic(void) 

 

  { 

       } 
 
 

double podwojenie(double a) 

 

  { 

        return (2.0 * a); 
       } 
 

background image

 32 

 

int dodaj(int a, int b) 

 

  { 

 

   int c; 

 
 

   . . . 

 

   return(a + b + c); 

 

  } 

 
Uwaga: 
 
 

Jeżeli  w  definicji  funkcji  nie  występuje  określenie  typu,  to 

funkcja jest typu 

int

 
 

Zmienne zadeklarowane wewnątrz funkcji są lokalne (istnieją 

tylko  w  tej  funckcji).  Inne  zmienne  używane  w  funkcji  są 
zmiennymi globalnymi. 
 
 

#include 

<stdio.h> 

 
int a = 33;  

/* zmienna globalna */ 

 
 
main() 
  { 
   int b = 77;  

/* zmienna lokalna */ 

 
   printf("%d %d\n", a, b); 
  } 

 
 

background image

 33 

7.2.

 

Instrukcja return 

 

 

instrukcja_return ::= return; | return 

wyrażenie ; 

 
 
Przykłady: 

 

return; 

 

return ++a; 

 

return (a + b); 

 
Uwaga: 


 

Wyrażenie po return może być ujęte w nawiasy. 



 

Wykonanie  instrukcji  return  powoduje  zakończenie  działania 
funkcji i przekazanie sterowania do instrukcji wywołuj
ącej daną 
funkcj
ę.  



 

Jeżeli instrukcja return zawiera wyrażenie, to wartość tego  



 

wyrażenia 

jest 

przekazywana 

również 

do 

instrukcji 

wywołującej. 



 

Jeżeli zachodzi potrzeba, to typ wartości jest zamieniany na typ 
zgodny z typem funkcji. 



 

Brak  instrukcji  return  w  funkcji  powoduje  zakonczenie  jej 
działania po napotkaniu znaku ko
ńca funkcji tj. } . 

 
Przykład. 

 

double wartosc_bezwzgledna(double x) 

 

  { 

 

   if (x >= 0.0) 

 

 

return x; 

 

   else 

 

 

return -x; 

       } 

background image

 34 

7.3.

 

Prototyp funkcji 

 

 

typ  

nazwa_funkcji (listałtypów_parametrów) 

 
Przyklady: 

 

double sqrt(double); 

 

void f(char c, int i); 

 

 

 

 

 

równoważne 

 

void f(char, int); 

7.4.

 

Wywołanie funkcji 

 
Wywołanie funkcji zawiera nazwę funkcji i listę parametrów aktualnych. 
 
Po wywołaniu funkcji następuje: 
1. Obliczne są wartości wyrażeń reprezentujących parametry aktualne. 
2. Typy obliczonych wartości wyrażeń są zamieniane na typy parametrów 
formalnych zgodnie z deklaracją funkcji. 
3. Wykonywana jest treść funkcji. 
4. Po napotkaniu instrukcji return lub znaku końca funkcji sterowanie 
powraca do miejsca wywołania funkcji. 
 
Uwaga. 

 

 

Parametry funkcji są przekazywane przez wartość

 
 

background image

 35 

7.5.

 

Reguły zasłaniania 

 
 

Identyfikatory lokalne zasłaniają identyfikatory globalne. 

 
Przykłady. 

 

 

 int a = 1, b = 2, c = 3; 

 

  printf("%d %d %d\n", a, b, c); 

/* 1 2 3 */ 

       a = b; 
 

   { 

         int b = 4; 
         float c = 5.0; 
       printf("%d %d %f\n", a, b, c); /* 1 4 5.0 */ 
         a = b; 
          { 
           int c; 
 
           c = b; 
 

    printf("%d %d %d\n", a, b, c); /* 4 4 4 */ 

          } 
   printf("%d %d %f\n", a, b, c);  

/* 4 4 5.0 */ 

        } 
 printf("%d %d %d\n", a, b, c); 

 

/* 4 2 3 */ 

    

    
     { 
 

 int a,b; 

 

 

. . . 

 

   { 

         float b; 
 

 

. . . 

 

   } 

 

 

. . . 

 

   { 

         float a; 
 

 

. . . 

 

   } 

 

  . . . 

 

}    

background image

 36 

7.6.

 

Rodzaje zmiennych 

 
 

Zmienne w języku C posiadają dwa atrybuty: typ i rodzaj. 

 
Słowa kluczowe określające rodzaj zmiennej: 
 

auto 

 

extern 

 

register   

static 

 
 

7.6.1.

 

Rodzaj auto (zmienne automatyczne) 

 
 

Zmienne zadeklarowane wewnątrz funkcji lub bloku są rodzaju auto, 

chba że ich rodzj został określony w deklaracji. (Słowo auto można 
opuszczać). 
 
 

Zmienne tego rodzaju istnieją tylko w czasie wykonywania funkcji 

lub bloku w którym zostały zadeklarowane. 
 
 

7.6.2.

 

Rodzaj extern (zmienne zewnętrzne) 

 
 

Zmienne zadeklarowane poza funkcjami są rodzaju extern. (Słowo 

extern można opuścić). Zmienne te są zmiennymi globalnymi. 
 
Użycie słowa kluczowego extern w deklaracji zmiennej oznacza 
poszukiwanie zmiennej o tej samej nazwie w całym programie (nawet w 
innych plikach). 
 
Przykład. 
 

Plik p1.c: 

 

#include <stdio.h> 

 

int a = 1, b = 2, c = 3; 

 

int f(void); 

 
 

main() 

 

  { 

 

     

printf("%3d\n", f()); 

 

 

printf("%d %d %d\n", a, b, c); 

     } 

 

Plik p2.c: 

background image

 37 

 

 

intf(void) 

 

 

  { 

 

 

   extern int a; 

 

 

   int b, c; 

 
 

 

   a = b = c = 4; 

 

 

   return (a + b + c); 

 

 

  } 

 
Uwaga. 
 

Użycie  zmiennych  globalnych  może  powodować  efekty 

uboczne działania funkcji. 
 
 
Wszystkie funkcje s
ą funkcjami zewnętrznymi. 
 
 

7.6.3.

 

Rodzaj register (zmienne rejestrowe) 

 
 

Słowo kluczowe register sugeruje kompilatorowi aby umieścić daną 

zmienną w rejestrze procesora. Czas dostępu do zmiennych rejestrowych 
jest najkrótszy. 
 
Przykład. 
 

 

 register int i; 

 
 

 for (i = 0; i < LIMIT; ++i) 

 

 

. . . 

   

 
Uwaga. 
 

register i; jest równoważne register int i; 

 
 

background image

 38 

7.6.4.

 

Rodzaj static (zmienne statyczne) 

 
 

Użycie w deklaracji zmiennej słowa static powoduje: 

 
1.   Dla zmiennej lokalnej - zachowanie wartości przy ponownym wejsciu 

do funkcji lub bloku, w którym ta zmienna jest zadeklarowana. 
 

2.   Dla zmiennych globalnych - ograniczenie zakresu danej zmiennej 

zewnętrznej od miejsca zadeklarowania do końca danego pliku. 

 
Przykłady: 

 

void f(void) 

 

  { 

 

   static int licz = 0; 

 
      ++licz; 
 

   if (licz % 22 == 0) 

 

 

. . . 

 

   else 

 

 

. . . 

 

  } 

 
 
 

void f(void) 

 

  { 

 

 

. . . 

 

  } 

 
 

static int i; 

 
 

void g(void) 

 

  { 

 

 

. . . 

 

  } 

 
Uwaga. 
 

Można deklarować rodzaj funkcji jako static ograniczając jej zakres 

do jednego pliku. 

background image

 39 

7.7.

 

Inicjowanie zmiennych 

 
 

Zmienne rodzaju extern i static są automatycznie inicjowane na 

wartość 0, chyba że w zostały zainicjowane w programie. 
 

Zmienne rodzaju auto i register nie są inicjowane. 

 

7.8.

 

Rekurencja 

 

Funkcja jest rekurencyjne, jeżeli w jej treści występuje wywołanie tej 

samej funkcji. 

 

Przykład. 
int sum(int n) 
  { 
   if (n <= 1) 
     

return n; 

   else 
 

return (n + sum(n-1)); 

  } 
 
sum(1)  

 

 

 

 

 

sum(2) 

2 + sum(1)  

 

sum(3) 

3 + sum(2) = 3 + (2+sum(1)) 6 

 
#include  

<stdio.h> 

main() 
  { 
   void pisz(void) 
   printf("Wprowadz tekst: "); 
   pisz(); 
   printf("\n\n"); 
  } 
void pisz(void) 
  { 
   int c; 
   if ((c = getchar()) != '\n') 
     pisz(); 
   putchar(c); 
  } 

background image

 40 

8.

 

Tablice 1-wymiarowe 

 
int a[rozmiar]; 

/* a[0], a[1], ... , a[rozmiar-1] */ 

 
 

8.1.

 

Inicjowanie tablic 

 

inicjalizator_tablcy_jednowym ::= { 
listałinicjatorów } 
listałinicjatorów ::= inicjator {, inicjator}

0+

 

inicjator ::= wyrażenie 

 
float f[5] = { 0.0, 1.0, 2.0, 3.0, 4.0 };  
 
Jeżeli lista inicjatorów jest krótsza od ilości elementów tablicy, to 
pozostale elementy tablicy są inicjowane na wartość 0. 
int a[100] = {0}; 
 
Niezainicjowane tablice rodzaju extern lub static są automatycznie 
inicjowane na wartość 0. 
 
Tablica zadeklarowana bez podania rozmiaru a zainicjowana ma ilość 
elementów równą ilości inicjatorów. 

int a[] = { 2 , 3, 5, -7}; 

 

 

 

 

równoważne 

int a[4] = { 2, 3, 5, -7}; 
 
char s[] = "abc"; 
char s[] = {'a', 'b', 'c', '\0'}; 

 

8.2.

 

Indeksowanie 

 
 

a[wyrażenie]  

 
Zakres indeksów tablicy nie jest kontrolowany. 
 

background image

 41 

8.3.

 

Wskaźniki 

 
 

x - zmienna   

&x - adres zmiennej 

 

int *p, i; 
 
 

p = 0; 

 

p = NULL;    

/* równowa

ż

ne p = 0; */ 

 

p = &i; 

 

p = (int *) 1776;   /* adres bezwzgl

ę

dny */ 

 
 
int a = 1, b = 2, *w; 
 
a   

 

 

 
  1 

 

 

  2  

   ----------> 

 
 
 

p = &a; 

a   

 

 

 
  1 

 

 

  2  

   ----------> 

 
 

b = *p; 

 

 

równowa

ż

ne 

 

b = a; 

 

Przykład. 

#include  

<stdio.h> 

 
main() 
  { 
   int i = 7, *w; 
 
 

w = &i; 

 

printf("%s%d\n%s%u\n", 

 

 

"Wartosc i : ", *w, 

 

 

"Adres i   : ", w); 

  } 

background image

 42 

 
Zmienne wskaźnikowe mogą być inicjowane. 

 

int i, *w = &i; 

 
Przykłady. 

 

int i = 3, j = 5, *p = &i, *q = &j, *r; 

 

double w; 

 
p == &i 
 

 

p == (&i)   

 

 

 

 

 

 

 

 

 

 

 

 

 
* * & p 
 

 

* (* (& p)) 

 

 

 

 

 

 

 

 

 
r  = & x 
 

 

r = (& x)   

 

 

 

 

 

 

 

 

ą

 
7 * * p / * q + 7 
 

 

((7 * (* p)) / (* q)) + 7 

 

 

 

 

 

 

 

 

11 

 
* (r = & j) *= * p 
 

 

(* (r = (& j))) *= (* p)   

 

 

 

 

 

 

 

 

15 

 

 

 

Uwaga. 
W ANSI C konwersja typów wskaźnikowych jest niedozwolona, chyba że 
jednym z typów jest typ void *  lub  prawym argumentem stała 0. 
int *p;  

float *q;   

void *v; 

 Poprawne 

 

 

 

 

 

Błędne 

p = 0; 

 

 

 

 

 

p = 1; 

p = (int *) 1;  

 

 

 

v = 1; 

p = v = q;   

 

 

 

p = q; 

p = (int *) q; 

 

background image

 43 

Operatora adresowania & nie wolno stosować do: 
  

- stałych 

 

 

 

 

&3 

 

- nazw tablic (nazwa tablicy jest stałą) 

 

 

 

int a[77]; 

 

 

 

&a 

 

- wyrażeń 

 

 

 

&(k + 99) 

 

- zmiennych rodzaju register 

 

 

 

register v; 

 

 

 

&v 

 
Operator adresowania może być użyty do elementu tablicy: 

 

&a[0] 

 

&a[i+j+3] 

 

8.4.

 

Przekazywanie parametrów przez referencję 

 
#include  

<stdio.h> 

void zamien(int *p, int *q) 
  { 
   int rob; 
 

rob = *p; 

 

*p = *q; 

 

*q = rob; 

  } 
 
main() 
  { 
   int i = 3, j = 5; 
   void zamien(int *, int *); 
 
 

zamien(&i, &j); 

 

printf("%d %d\n", i, j); 

 

/* 5 3 */ 

  } 

 
Aby osiągnąć efekt przekazania parametru przez referencję należy: 
1. Zadeklarować parametr funkcji jako wskaźnik. 
2. W treści funkcji użyć wartości wskazywanej przez wskaźnik. 
3. Przekazać w wywołaniu jako argument adres. 

background image

 44 

8.5.

 

Tablice a wskaźniki 

 
Nazwa tablicy reprezentuje adres. 
Wskaźniki można indeksować. 
 

 

a[i] 

 

równoważne

 

 

*(a + i) 

 

p[i]  

 

 

 

 

*(p + i) 

 

Przykład. 

#define  

N  100 

 
int a[N], i, *p, sum = 0; 
 

p = a; 

 

rówoważne

 

 

p = &a[0]; 

 
p = a + 1;   

 

 

p = &a[1]; 

 
for (p = a; p < &a[N]; ++p) 
 

sum += *p; 

 
for (i = 0; i < N; ++i) 
 

sum += *(a + i); 

 
p = a; 
for (i = 0; i < N; ++i) 
 

sum += p[i]; 

 

Wyrażenia niepoprawne: 

 

a = p 

++a 

 

a += 2  &a 

 
 

8.6.

 

Arytmetyka na wskaźnikach 

 

double a[2], *p, *q; 
p = a; 
q = p + 1;   

 

 

 

 

/* q = &a[1] */ 

printf("%d\n", q-p); 

 

 

 

/* 1 */ 

printf("%d\n", (int) q - (int) p);  

/* 8 */ 

 

background image

 45 

8.7.

 

Tablice jako parametry funkcji 

 
Tablice przekazuje się jako parametry w postaci wskaźników. 
 

double sum(double a[], int n) 
  { 
   int i; 
   double sum = 0.0; 
 
 

for (i = 0; i < n; ++i) 

 

  sum += a[i]; 

 

return sum; 

  } 
 

 

 

 

lub 

double sum(double *a, int n) 
  { 
    . . . 
 

Przykłady wywołań:

 (double v[100]) 

sum(v, 100)  

v[0]+v[1]+...+v[99] 

sum(v,88) 

 

 

V[0]+V[1]+...+V[87] 

sum(&v[7],k-7)  

v[7]+v[8]+...+v[k-1] 

sum(v+7, 2*k)   

v[7]+v[8]+...+v[2*k-6] 

 

8.8.

 

Napisy 

 
char *p = "abc"; 
 
printf("%s %s\n", p, p+1);  
 

 

 

 

/* wydrukuje sie abc bc */ 

 

 

 

"abc"[1] 

 

*("abc" +2)  - wyra

ż

enia poprawne 

 
char *p = "abcde";  

 

char s[] = "abcde"; 

 
p   

 

 

 

 

 

  a b c d e \0   

 

a b c d e \n 

 
 
Przykład. 

background image

 46 

/* Zliczanie słów w napisie */ 
 
#include 

<ctype.h> 

 
int licznik_slow(char *s) 
  { 
   int licz = o; 
 
 

while (*s != '\0') 

 

  { 

 

   while (isspace(*s))  /* pomijanie spacji */ 

 

 

++s; 

 

   if (*s != '\0')   

/* znaleziono słowo */ 

 

 

 

 

 ++licz; 

 

 

 while (!isspace(*s) && *s != '\0') 

 

 

 

++s; 

 

/* akceptowanie słowa */ 

    

 

  } 

 

return licz; 

  } 

 
 

8.8.1.

 

Przetwarzanie napisów  

 #include <string.h> 

 
char *strcat(char *s1, const char *s2); 
 

Konkatenacja napisów s1 i s2. Wynikiem jest napis s1. 

 
int strcmp(const char *s1, const char *s2); 
 

Porównywanie napisów w porządku leksykograficznym. Wartością 

funkcji jest liczba ujemna jeżeli napis s1 poprzedza napis s2, zero gdy 
napisy są identyczne, lub liczba dodatnia (napis s2 jest wcześniejszy niż 
s1). 
  
char *strcpy(char *s1, const char *s2); 
 

Kopiowanie napisu s2 do s1. 

 

background image

 47 

unsigned strlen(const char *s); 
 

Wartością funkcji jest liczba znaków napisu s. 

 
 

unsigned strlen(const char *s) 
  { 
   register int n; 
 

 

 

for (n = 0; *s != '\0'; ++s) 

 

 

++n; 

 

return n; 

  } 
 
 
char *strcat(char *s1, const char *s2) 
  { 
   register char *p = s1; 
 
 

while (*p) 

 

 

++p; 

 

while (*p++ = *s2++) 

 

 

 

return s1; 

  } 

Przykłady. 

char 

s1[50] = "duzy ladny sloneczny dom", 

 

s2[] = "maly pokoj"; 

 
strlen(s1);  

 

 

 

 

24 

strlen(s2 + 5); 

 

 

 

 

strcmp(s1, s2); 

 

 

 

 

warto

ść

 ujemna 

 
printf("%s",s1 + 11);   

 

sloneczny dom 

 
strcpy(s1 + 5, s2 +5); 
strcat(s1," bez okien"); 
printf("%s",s1); 

 

 

 

duzy pokoj bez okien 

background image

 48 

9.

 

Tablice wielowymiarowe 

 

 

int a[100];  

/* 100 elementów */ 

 

int b[2][7]; 

 

/* 14 elementów */ 

 

int c[5][3][2]; /* 30 elementów */ 

 

 

Tablice są lokowane w spójnych obszarach pamięci. 

 

9.1.

 

Tablice 2-wymiarowe 

 

int a[3][5]; 

 
 

 

kol.1 

kol.2 kol.3 kol.4 kol.5 

w.1  

a[0][0] 

a[0][1] 

 

 

 

 

a[0][4] 

w.2  

a[1][0] 

a[1][1] 

 

 

 

 

a[1][4] 

w.3  

a[2][0] 

a[2][1] 

 

 

 

 

a[2][4] 

 

 

Wyra

ż

enia równowa

ż

ne do a[i][j]: 

 

 

*(a[i] + j) 

 

 

(*(a + i))[j] 

 

 

*((*(a +i)) + j) 

 

 

*(&a[0][0] + 5*i + j) 

Uwaga. 
a oznacza 

&a[0],

 a nie 

&a[0][0]

 

 
Jeżeli parametrem formalnym funkcji jest tablica wielowymiarowa, to 
muszą być podane wszystkie wymiary za wyjątkiem pierwszego. 
 
Przykład. 

int sum(int a[][5]) 
  { 
   int i, j, sum = 0; 
 

for (i = 0; i < 3; ++i) 

 

  for (j = 0; j < 5; ++j) 

 

 

sum += a[i][j]; 

 

return sum 

  } 
 
int a[][5]  int (*a)[5] 

int a[3][5] 

background image

 49 

9.2.

 

Tablice 3-wymiarowe 

 

 

int a[7][9][2]; 

 
a[i][j][k]   

*(&a[0][0][0] + 9*2*i + 2*j +k) 

 
int sum(int a[][9][2]) 
  { 
   int i, j, k, sum = 0; 
 

 

 

for (i = 0; i < 7; ++i) 

 

  for (j = 0; j < 9; ++j) 

 

    for (k = 0; k < 2; ++k) 

 

 

sum += a[i][j][k]; 

 

return sum 

  } 
 
int a[][9][2]   

int (*a)[9][2]  

int 

a[7][9][2] 
 
INICJALIZACJA TABLIC WIELOWYMIAROWYCH 
 
int a[2][3] = {1, 2, 3,  4, 5, 6}; 
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; 
int a[][3] = {{1, 2, 3}, {4, 5, 6}}; 
 
int a[2][2][3] = { 
 

 

{{1, 1, 0}, {2, 0, 0}}, 

 

 

{{3, 0, 0}, {4, 4, 0}} 

 

 

 

  }; 

 
int a[][2][3] = {{{1, 1}, {2}}, {{3}, {4, 4}}}; 

int a[2][2][3] = {0}; - 

zerowanie

 

background image

 50 

9.3.

 

Tablice wskaźników 

9.4.

 

Nierówne tablice 

 
 
#include 

<stdio.h> 

 
main() 
  { 
   char a[2][15] = {"abc","defgh"}; 
   char *p[2] = {"abc","defgh"}; 
 
  

. . . 

 

9.5.

 

Funkcje jako argumenty 

 

 

(k)

f

2

n

m

=

k

 

 
 

k

1

=

f(k)

 

 
 

(k)

=

f(k)

sin

 

 

double sum_kw(double f(double), int m. int n) 
  { 
   int k; 
   double sum = 0.0; 
 
 

for (k = m; k <= n; ++k) 

 

  sum += f(k) * f(k); 

 

return sum; 

  } 
 

równoważnie 

 
double sum_kw(double (*f)(double), int m, int n) 
  { 
    . . . 

ale 

background image

 51 

 

double *f(double); 
sum += f(k) * f(k)  albo  sum += (*f)(k) * (*f)(k) 

 
 

f - wskaźnik do funkcji 

 

*f - sama funkcja 

 

(*f)(k) - wywołanie funkcji 

 
Wywołanie. 
 

#include 

<stdio.h> 

 
main() 
  { 
   double f(double), g(double), 
      

sum_kw(double (*)(double), int, int); 

 
 

printf("funkcja f: %.7f\n", sum_kw(f,1,10000); 

     printf("funkcja sinus: %.7f\n", 
sum_kw(sin,2,15); 
  } 
 
double f(double x) 
  { 
   return 1.0 / x; 
  } 

 
 

background image

 52 

10.

 

Argumenty main 

 

#include        <stdio.h> 
 
void main(int argc, char *argv[],char *env[]) 
  { 
    int i; 
 
    printf("argc = %d\n", argc); 
    for (i = 0; i < argc; ++i) 
      printf("argv[%d] = %s\n", i, argv[i]); 
 
  while(*env) 
      printf("%s\n",*env++ ); 
  } 
 

argc  

- ilość argumentów (argc >= 1) 

argv[0]   - nazwa programu  

( w MS DOS z pełną nazwą ścieżki  
    całość pisane wielkimi literami ) 

env 

 - zmienne środowiskowe 

 

 

background image

 53 

11.

 

Definiowanie typów 

 
typedef 

char * string; 

typedef 

int 

 

CAL, STOPA, YARD; 

typedef 

float  wektor[10]; 

typedef 

double (*WFD)(double); 

 

 

 

 /* wsk. do funk. double */ 

 
CAL  

dl, szer; 

string  

s1 = "abc", s2="xyz"; 

wektor  

x; 

 

 

 

float x[10]; 

WFD  

f; 

 

 

 

double (*f)(double); 

 
#define 

N  3 

 
typedef 

double  

scalar; 

typedef 

scalar wektor[N]; 

typedef  

scalar tablica[N][N]; 

 

 

 

 

/* lub typedef wektor 

tablica[N]; */ 
 
void dodaj(wektor x, wektor y, wektor z) 
  { 
   int i; 
 

for (i = 0; i < N; ++i) 

 

 

z[i] = x[i] + y[i]; 

  } 
 
scalar il_skal(wektor x, wektor y) 
  { 
   int i; 
   scalar sum = 0.0; 
 
 

for (i = 0; i < N; ++i) 

 

 

sum += x[i] * y[i]; 

 

return sum; 

  } 
void mnm(tablica a, tablica b, tablica c) 

background image

 54 

  { 
   int i, j, k; 
 
 

for (i = 0; i < N; ++i) 

 

  for (j = 0; j < N; ++j) 

 

    { 

         

a[i][j] = 0.0; 

 

 

for (k = 0; k < N; ++k) 

 

 

  a[i][j] += b[i][k] * c[k][j]; 

         } 
  } 

 
 

background image

 55 

12.

 

Struktury 

 

deklaracja_struktury ::= 
specyfikacja_struktury 
                               listałdeklaratorów; 
specyfikacja_struktury ::= struct nazwa | 
 

struct nazwa

op

 { { deklaracja_pola }

1+

 } 

nazwa ::= identyfikator 
deklaracja_pola ::= typ listałdeklaracji; 
listałdeklaratorów ::= deklarator {, deklarator 
}

0+

 

 
 

struct karta 
  { 
   int wart; 
   char kolor; 
  }; 

 

 

 

 

 

definicja typu 

 

struct karta k1, k2; 

 
 

 

 

 

lub 

 
struct karta 
  { 
   int wart; 
   char kolor; 
  } k1, k2; 
 
k1.wart = 3; 
k1.kolor = 'p'; 

 

k2 = k1; 
 
typedef struct karta karta; 
 
karta k3, k4, k5; 

background image

 56 

 
struct owoc 
  { 
 

char *nazwa; 

 

int kalorie; 

  }; 
struct warzywo 
  { 
   char *nazwa; 
   int kalorie; 
  }; 
struct owoc a; 
struct warzywo b; 
a.nazwa 

 

b.nazwa 

 
struct karta 
  { 
   int wart; 
   char kolor; 
  } talia[52]; 
 
struct 
  { 
   int 

dzien, miesiac, rok; 

   char 

*nazwa_dnia[7]; 

   char   *nazwa_mies[12]; 
  } wczoraj, dzis, jutro; 
 
struct data 
  { 
   int 

dzien, miesiac, rok; 

   char 

*nazwa_dnia[7]; 

   char   *nazwa_mies[12]; 
  }; 
struct data wczoraj, dzis, jutro; 
typedef struct  

 

 

 

 

 

 float re; 

 

 

 float im; 

 

 

} complex; 

complex a, b, c[100]; 

background image

 57 

12.1.

 

Dostęp do skłdników struktury 

 

 

zmienna_strukturalna.nazwa_pola 

 
Plik stud.h 
 

#define 

LICZBA_STUD 

100 

 
struct student 
   { 
    char *nazwisko; 
    int *nr_indeksu; 
    float ocena; 
   }; 

 

 

Plik stud.c 
 

#include "stud.h" 
 
main() 
  { 
   struct student st, rok[LICZBA_STUD]; 
   int i, licz = 0; 
 

. . . 

 

st.ocena = 4; 

 

st.nazwisko = "Kowalski"; 

 

st.nr_indeksu = 91017; 

 

. . . 

 

for (i = 0; i < LICZBA_STUD; ++i) 

 

  licz += rok[i].ocena == 2; 

 

. . . 

 
Jeżeli wartością zmiennej wskaźnikowej jest adres struktury, to do jej 
składników można odwołać się przez: 
 
 

wskaźnik_do_struktury -> nazwa_składnika 

 
co jest równoważne odwałaniu 
 

(*wskaźnik_do_struktury).nazwa_składnika 

 

 

background image

 58 

Uwaga. 
 

Nawiasy są konieczne. W przeciwnym przypadku otrzymamy 

 

 

 

*(wskaźnik_do_struktury.nazwa_składnika) 

  
co jest błędne, gdyż tylko do struktury można użyć  operatora ".", a nie do 
wskaźnika do struktury. 
 
Przykład.  
Dodawanie liczb zespolonych. 
 
Plik complex.h: 
 

struct complex 
  { 
 

double re; 

 

double im; 

  }; 
 
typedef 

struct complex complex; 

 

Plik complex.c: 
 

#include 

"complex.h" 

 
void dodaj(complex *a, complex *b, complex *c) 
  { 
 

a -> re = b -> re + c -> re; 

 

a -> im = b -> im + c -> im; 

  }; 
 
Przykład: 
struct student st, *p = &st; 
 
st.ocena = 4; 
st.nazwisko = "Kowalski"; 
st.nr_indeksu = 91017; 
 
st.ocena 
 

 

p->ocena 

 

 

 

 

4.0 

background image

 59 

 
(*p).nr_indeksu 
 

 

p->nr_indeksu   

 

 

91017 

 
*p->nazwusko + 1 

 

 

(*(p->nazwisko))+1   

 

 

 
*(p->nazwisko + 2) 
 

(p->nazwisko)[2] 

 

 

 

 
 

12.2.

 

Struktury i funkcje 

 
 

Struktury tak jak wszystkie parametry są przekazywane przez wartość. 

W wywołanej procedurze powsaje lokalna kopia. Przy dużych strukturach 
może to być nieefektywne. Lepiej jest przekazywać adres do struktury. 
 
Przykład. 
 

typedef struct 
  { 
 

char 

 

 

 

nazwisko[25]; 

 

int 

 

 

 

nr_id; 

 

struct wydz  

wydzial; 

 

struct adres_dom 

*a_d; 

 

double 

 

 

pensja; 

 

. . . 

  } pracownik; 
 
struct wydz 
  { 
   char nazwa[20]; 
   int kod; 
  }; 
 
I sposób: 
pracownik wczytaj_dane(pracownik p) 
  { 
    

. . . 

 

printf("Kod wydzialu: "); 

background image

 60 

 

scanf("%d", &k); 

 

p.wydzial.kod = k; 

   . . . 
 

return p; 

  } 
 
p.wydzial.kod     (p.wydzial).kod 
 
main() 
  { 
   pracownik p; 
 

. . . 

 

p = wczytaj_dane(p); 

 

/* dwa kopiowania */ 

 
II sposób: 
void wczytaj_dane(pracownik *p) 
  { 
    

. . . 

 

printf("Kod wydzialu: "); 

 

scanf("%d", &k); 

 

p->wydzial.kod = k; 

   . . . 
  } 
 
p->wydzial.kod     (p->wydzial).kod 
 
main() 
  { 
   pracownik p; 
 

. . . 

 

wczytaj_dane(&p); 

 

 

background image

 61 

12.3.

 

Inicjalizacja struktur 

karta k = {8,'p'};  

/* ósemka pik */ 

 
complex a[3][3] =  
 

 

 {{1.0, -0.1},{2.0, 0.2}, (3.0, 0.3}}, 

 

 {{4.0, -0.5},{5.0, 0.5}, (6.0, 0.6}}, 

   };  /*  a[2][] zerowanie */ 
 
struct owoc o = {"gruszka", 150}; 
 
struct adres_dom  
  { 
 

char *ulica; 

 

char *miasto; 

 

long kod: 

  } adres = {"Wiejska 45A", "Bialystok", 15351}; 
 
struct adres_dom a_poprz = {0}; 

 
 

background image

 62 

13.

 

Unie 

Skladnia unie  bazuje  na strukturach. 
 

 

 

union      u-tag { 

 

 

 

int    inval; 

 

 

 

float  fval  ; 

 

 

 

char   *pval; 

 

 

} uval; 

 
 

Zmienna 

uval

 bedzie posiadac wystarczajacy  obszar  pamieci 

aby  przechowac  najwiekszy  z  trzech  typow  
 
 Dowolny  typ  z tych  trzech  typow  moze  byc  przypisany  zmiennej  
uval.   Na programiste naklada sie odpowiedzialnosc sledzenia tego jaki 
typ jest aktualnie zapamietany w uni. 
 
 

 

Dostęp do  skladnikow  uni  

 

 

 

nazwa-uni.skladnik 

lub 

 

 

wskaznik-uni->skladnik 

 
czyli tak samo jak do skladnikow struktur. 
 
 Jesli  zmienna  utype jest  uzywana  do  sledzenia  biezacego  typu  
zapamietanego   w zmiennej uval, to mozna napisac: 

 
 

 

if (utype == INT) 

 

 

 

printf("%d\n", uval,ival); 

 

 

else if (utype == FLOAT) 

 

 

 

printf("%f\n", uval,fval); 

 

 

else if (utype == STRING) 

 

 

 

printf("%s\n", uval,pval); 

 

 

else 

 

 

 

printf("zly typ %d w typzl\n", 

utype); 

background image

 63 

 

Unie moga wystepowac wewnatrz struktur i tablic.   

 
Notacja dostepu  do  skladnikow  uni  w  strukturze  jest  taka  sama jak  
notacja dostepu do skladnikow w zagniezdzonych strukturach.  
 
Na przyklad w tablicy strukturalnej zdefiniowanej nastepujaco: 

 

 

struct { 

 

 

 

 

char *name; 

 

 

 

 

int flags; 

 

 

 

 

int utype; 

 

 

 

 

unlon { 

 

 

 

 

 

int   ival; 

 

 

 

 

 

float fval; 

 

 

 

 

 

char *pval; 

 

 

 

 

} uval; 

 

 

} symtab [NSYM]; 

 
zmienna ival jest dostepowana przez 

 
 

symtab [i].uval.ival 

 
natomiast pierwszy znak lancucha pval przez 

 

 

*symtab [i].uval,ival 

 
Operacjami dozwolonymi na uniach sa:  


 

dostęp do skladnika  



 

pobieranie adresu.  

 
Unie nie  moga  byc  sobie  przypisywane, przesylane do funkcji ani tez 
przez funkcje zwracane.   
 
Wskazniki na unie  sa  uzywane  w  taki  sam  sposob  jak  wskazniki  na 
struktury. 

background image

 64 

13.1.

 

Pola bitowe 

Składniki struktur i uni typu int lub unsigned mogą być deklarowane z 
określeniem ilości bitów przeznaczonych na reprezentację danego 
składnika. 
 
Przykład. 
 

struct Data 
  { 
 

unsigned d: 5; 

 

unsigned m : 4; 

 

unsigned r : 7; 

  }; 
 
struct Data d; 
 
d.r=99; 
d.m=10; 
d.d=1; 

 
Składnia: 
 

pole bitowe ::= { int | unsigned }

1

 {identyfikator}

opt

 : wyr 

 

wyr ::= stała_calkowita 

 
 
Uwagi: 
 
1. Nie wolno deklarować tablic pól bitowych. 
2. Pole bitowe bez nazwy oznacza przejście do następnego słowa. 
 

 

struct abc 

 

  { 

 

   unsigned a : 1, :0, b : 1, : 0, c : 1; 

 

  } 

background image

 65 

14.

 

Modele pamieci 

 Rozdział ten dotyczy kompilitorów  
Turbo C, Borland C dla systemu MS-DOS. 

 
Specyfikacja modelu pamięci który będzie używany przez program. Model ten 
determinuje sposób adresowania zmiennych. 
 

  

Default = Small 

Segments 

Pointers 

Memory 

Model 

Code 

Data  Stack  Code 

Data 

BCC 

Option 

Tiny 

64K 

near 

near 

-mt 

Small 

64K 

64K 

near 

near 

-ms 

Medium 

1MB 

64K 

far 

near 

-mm 

Compact 

64K 

1MB 

near 

far 

-mc 

Large 

1MB 

1MB 

far 

far 

-ml 

Huge 

1MB 

64K 

each 

64K 

stack 

far 

far 

-mh 

   

 

  

Tiny

 

  

 

Modelu Tiny używa się do bardzimałych programów. 
Wszystkie rejestry segmtowe (CS, DS, ES, SS) są ustawione na te same adresy, 
wobec tego możesz używać łącznie 64K dla danych, kodu i stost. Wskaźniki są 
zawsze bliskie (near). 
Model Tiny umożliwia generowanie programów konwertowalnych do .COM z 
wykorzystaniem linkera z opcją TLINK /t.  
 
 

 Small

 

Modelu Smal używaj do średniej wielkości aplikacji. 
Dane i kod są umieszczane w rozłącznych segmentach więc dla kodu jest 64K i 
kolejne 64KB dla danych i kodu. 
Domyslnie są używane wskaźniki near. 

background image

 66 

Medium

  

 
Jest to najlepszy model dla dużych programów, które nie przechowują w pamieci 
dużo danych. 
 
Dalekie wskaźniki są domyślne dla kodu, ale nie dla danych. Dane i stos są 
ograniczone do 64K ale kod może zajmować do 1MB. 
 
 

 Compact

 

 

Modelu Compact używa się gdy program ma mały kod ale potrzebyje adresów do 
dużej ilości danych. 
Model ten jest przeciwieństwem modelu Medium: Dalekie wskaźniki są domuślne 
dla danych ale nie dla kodu 
Kod zajmuje do 64 Kb podczas gdy dane do 1MB. 
 
Wszystkie funkcje są domyślnie near a wskaźnki na dane far. 

 
   

Large

 

 
 
 Tego modelu używa się tylko do bardzo dużych aplikacji. Wskaźniki zarówno 
dodanych jak i do kodu są dalekie (far) a wielkości wspólnie są ograniczone do 1MB. 
Wszystkie funkcje domyślnie są far. 

 
 
 

Huge

 

 
Tego modelu używa się tylko do bardzo dużych aplikacji. Wskaźniki zarówno 
dodanych jak i do kodu są dalekie (far).Wszystkie funkcje domyślnie są far. 
Borland C++ normalnie limituje wszystkie dane do 64 KB. W modelu Huge tak nie 
jest. Dopuszczalne jest aby pojedyńcze dane zajmowały ponad 64KB. 
Modal ten dopuszcza wielosegmentowe dane (każdy segmeny 64KB), do 1MB kodu 
i 64KB stos. Wszystkie funkcje i wskaźniki są domyślnie far. 
 
 
 

background image

 67 

Tiny 

 
 
 
     

 64 KB 

                          .COM 

 

Small 

 64 KB 

 
 
 

 64 KB 

 
 

Medium 

      N* 

 64 KB 

 
 
 

 64 KB 

 
 
 

Compact 

 64 KB 

 64 KB 

 64 KB 

 
 

Large 

N * 

 64 KB 

 
  

 64 KB 

 64 KB 

 
 

Huge 

 N* 

 64 KB 

 
N * 

 64 KB 

 

 64 KB 

 

 

Kod 
Dane 
Heap 

 

 
Stos  

 

Kod 
Dane 
Heap 

 

 
Stos 

 

Far Heap 

 

Kod 
Kod 
Dane 
Heap  

 

 
Stos 

 

Far Heap 

 

Kod 
Dane 
Stos  

 

Heap 

 

Kod 
Kod 
Dane 
Stos 

 

Heap 

 

Kod 
Kod 
Dane 
Dane 
Stos 

 

Heap  

 

background image

 68 

15.

 

Dynamiczny przydział pamięci 

15.1.

 

Funkcje alokacji pamięci 

void * malloc(size_t size); 

••••

 

przydziela obszar wielkości size  

••••

 

zwraca wskaźnik do przydzielonego obszaru 

••••

 

typ wskaźnika void * może być automatycznie konwertowany do 
dowolnego typu wskaźnikowego. 

••••

 

jeśli brak odpowiedniej ilości pamięci to zwraca NULL 

void *calloc(size_t nitems, size_t size); 

••••

 

przydziela obszar wielkości nitimes*size  

••••

 

dane przydzielonego bloku są ustawione na 0 

void *realloc(void *block, size_t size); 

••••

 

 zmienia przydział pamięci dla obszaru blok na wielkości size 

••••

 

umożliwia zwiększenie bądź zmniejszenie wcześniejszego 
przydziału pamięci 

••••

 

dane z realokowanego obszaru są kopiowane 

••••

 

jeśli size wynosi 0 to zwalnia pamięć przydzieloną dla block 

••••

 

jeśli block równa się NULL to funkcja działa jak malloc 

void free(void *block); 

••••

 

zwalnia przydział pamięci dla obszaru blok przydzielony wcześniej 
przez malloc, calloc lub realloc 

unsigned coreleft(void); 

••••

 

szacuje ilość wolnego miejsca na stercie 

••••

 

(DOS)dla modeli Compact Large i Huge istnieje  funkcja 

 

     unsigned long coreleft(void); 

 
void *memset(void *s, int c, size_t n); 

••••

 

ustawia n bajtów począwszy od *s na wartość c 

••••

 

#include <mem.h> 

 
Odpowiedniki powyższych funkcji do obsługo dalekiej sterty(dotyczy 
Small i Medium) 
void far *farmalloc(unsigned long nbytes); 
void far *farcalloc(unsigned long nunits, unsigned long unitsz); 
void farfree(void far * block); 
unsigned long farcoreleft(void); 
void far * far _fmemset (void far *s, int c, size_t n) 

background image

 69 

15.2.

 

Tablice dynamiczne 

 
int t[20]; 

tablica 20 elementów typu int 

int *p; 

wskaźnik na typ int; 

 

main() 

int *p,i,N; 
 printf("Podaj ilo

ść

 elementów\n"); 

 scanf("%d",&N); 
 p=(int*)calloc(N,sizeof(int)); 

/* p=malloc(20* sizeof(int)); 

 

   memset(p,0,20*sizeof(int)); */ 

 for(i=0;i<N;i++) 
 

 

 *(p+i)=rand()%198-99; 

 
 for(i=0;i<N;i++) 
 

 

 printf("%4d",*(p+i)); 

printf("\n"); 
 realloc(p,2*N*sizeof(int)); 
 for(i=N;i<2*N;i++) 
 

 

 *(p+i)=rand()%198-99; 

for(i=0;i<2*N;i++) 
 

 

 printf("%4d ",*(p+i)); 

free(p); 

 
char* srev1(const char *s)                   
 

 

 

/* odwracanie napisu*/ 


char *p; 
int l=slen(s); 
 p=malloc(l+1); 
 p=p+l; 
 *p=0; 
  for(;*s;) 
 

 *p--=*s++; 

return ++p; 

background image

 70 

16.

 

Struktury listowe 

 

struct lista 
  { 
 

int dane; 

 

struct lista *nast; 

  } a; 
 
 

                 ------->       
 

dane 

 

nast 

 
 

struct lista b,c; 
 
a.dane = 1; 
b.dane = 2; 
c.dane = 3; 
a.nast = b.nast = c.nast = NULL; 
 
 

 

NULL 

 

NULL 

 

NULL 

 
 

a.nast = &b; 
b.nast = &c; 
 

 

  ----> 

  ----> 

NULL 

 

a.nast->dane 

 

 

 

 

a.nast->nast->dane  

 

 

 

 
NULL  - “zerowy”  wskażnik   
 
Zdefiniowany przez dołączenie jednego z plików 
alloc.h mem.h  stddef.h  stdio.h  stdlib.h   
a fizycznie zdefiniowane w  _null.h 

background image

 71 

16.1.

 

Listy liniowe 

Plik lista.h: 

 
#include <stdlib.h> 
typedef 

char 

 

Dane; 

typedef 
struct lista_lin 
  { 
 

Dane 

    

d; 

 

struct lista_lin 

*nast; 

  } 
     Element,  
    *Lista;

 

16.1.1.

 

Dynamiczny przydział pamięci 

#include <stdlib.h>

   lub   

#include<alloc.h>

 

 
 

void *malloc(size_t size); 

 
Funkcja daje jako wskaźnik do obszaru pamięci o wielkońci rozmiar  
(w bajtach). Wynik jest typu void * , konwresja do konkretnego tupu nie 
jest konieczna. 
 

Lista pocz; 
 
pocz = (Lista)malloc(sizeof(Element)); 
pocz -> d = 'n'; 
pocz -> nast = NULL; 
 

 

pocz 

  ------>     n   

NULL 

 

pocz ->nast = (Lista)malloc(sizeof(Element)); 
pocz ->nast ->d = 'a'; 
pocz ->nast ->nast = NULL; 
 

 

 

pocz 

 ------->     n   

  ----> 

NULL 

 

background image

 72 

16.1.2.

 

Operacje na listach 

 
1)

 

Tworzenie listy 

2)

 

Obliczanie liczby Elementów 

3)

 

Szukanie Elementu 

4)

 

Łączenie list 

5)

 

Wstawianie Elementów 
a)

 

pomiędzy dwa wskazane Elementy 

b)

 

po wskazanym elemencie 

c)

 

po elemencie danych 

d)

 

przed elemantem danych 

6)

 

Przetworzenie (np. wypisanie) wszystkich Elementów listy 

7)

 

Usuwanie Elementów 

8)

 

Kasowanie całej listy 

9)

 

Odwracanie listy jednokierunkowej 

 
 

#include 

<stdlib.h> 

#include 

"lista.h" 

 
/* Tworzenie listy - wersja rekurencyjna */ 
 
Lista napis_na_liste(char s[]) 
  { 
   Lista 

pocz; 

 
 

if (*s) 

 

  { 

 

   pocz = (Lista)malloc(sizeof(Element)); 

 

   pocz -> d = *s; 

 

   pocz -> nast = napis_na_liste(s+1); 

 

   return pocz; 

 

  } 

 return NULL; 
 
  } 
 

background image

 73 

/* Tworzenie listy - wersja iteracyjna */ 
 
Lista np_na_lst(char s[]) 
  { 
   Lista 

pocz = NULL, kon; 

 
 

if (s[0]) 

 

  { 

 

 

   pocz = (Lista)malloc(sizeof(Element)); 

 

   pocz -> d = *s; 

 

   kon = pocz; 

 

   for (; *++s ; ) 

 

 

 

 

 kon -> nast = (Lista)malloc   

 

 

 

 

 

 

 

(sizeof(Element)); 

 

 

 kon = kon -> nast; 

 

 

 kon -> d = *s; 

 

 

 

   kon -> nast = NULL; 

 

  } 

 

return pocz; 

  } 

 
 

16.1.3.

 

Przetwarzanie list 

 

/* ilosc Elementów - wersja rekurencyjna */ 
  
int ilosc_Elementow(Lista pocz) 
  { 
    

if (pocz == NULL) 

 

  return 0; 

 

else 

 

  return (1 + ilosc_Elementow(pocz -> nast)); 

  } 
 

background image

 74 

/* ilosc Elementów - wersja iteracyjna */ 
  
int il_el(Lista pocz) 
  { 
   int licz = 0; 
 

 

 

for ( ; pocz ; pocz = pocz ->nast) 

 

  ++licz; 

 

return licz; 

  } 
 
/* Ł

ą

czenie list */ 

void lacz_r(Lista a, Lista b) 
  { 
   if(a==NULL) a=b; 
   else 
 

if (a -> nast == NULL) 

 

  a -> nast = b; 

 

else  

 

  lacz(a -> nast, b); 

  } 
 
void lacz(Lista a, Lista b) 
  { 
 

if (a) 

 

 { 

 

  for(;a->nast;a=a -> nast) ; 

 

  a->nast=b; 

 

 } 

 

 else a=b; 

  } 
 
/* wstawianie Elementu */ 
/* wstawianie pomiedzy dwa wskazane Elementy */ 
 
void wstaw(Lista e1, Lista e2, Lista q) 
  { 
   q -> nast = e2; 
   e1 -> nast = q; 
  } 
 

background image

 75 

 
         e1             e2 
                   
 
             q 
        
 
 
/* Szukanie Elementu  
 pocz 

–  

pocz

ą

tek listy 

 e 

 

 pop 

- poprzedni Element listy   */ 

 
Lista szukaj(Lista pocz,Dane e,Lista *pop) 

 *pop=NULL; 
 while(pocz&& pocz->d!=e) 
  { 
  *pop=pocz; 
  pocz=pocz->nast; 
  } 
 return pocz; 

 
 
         e            e->nast 
                   
 
              
        
 
 
/* wstawianie po wskazanym elemencie */ 
void wstaw_po(Lista e, Dane x) 
  { 
 

Lista q=(Lista)malloc(sizeof(Element)); 

 

q->d=x; 

 

q -> nast = e->nast; 

 

e -> nast = q; 

  } 
 

 

 

 

background image

 76 

 
pocz    psz            psz->nast 
                   
 
              
        
          
newp 
 
pocz            pop     psz 
                   
 
              
                                  newp 
 
/* wstawianie po elemencie danych */ 
void wstaw_po_d(Lista *pocz,Dane s, Dane x) 

 Lista psz,pop,newp; 
 psz=szukaj(*pocz,s,&pop); 
 

 newp=(Lista)malloc(sizeof(Element)); 

 

 newp->d=x; 

 

 newp->nast=psz; 

 

 if(psz) 

 

  psz->nast=newp; 

 

 else 

 

  pop->nast=newp; 


pocz   pop                  psz 
                   
 
              
           
          newp 
 
 
  pop   pocz psz 
                   
 
              
           
       newp 

 

 

 

 

 

 

 

 

background image

 77 

/* wstawianie przed Elementem danych */ 
void wstaw_przed_d(Lista *pocz,Dane s, Dane x) 

 Lista psz,pop,newp; 
 psz=szukaj(*pocz,s,&pop); 
 

 newp=(Lista)malloc(sizeof(Element)); 

 

 newp->d=x; 

 

 newp->nast=psz; 

 if(pop) 
 

 pop->nast=newp; 

 else 
 

 *pocz=newp; 


 
/*wypisz Elementy listy */ 
void pisz_liste(Lista pocz) 

  printf("\n"); 
 
 while(pocz) 
 { 
  printf("%c ",pocz->d); 
  pocz=pocz->nast; 
 } 

 
/* kasowanie Elementu */ 
 
Lista usun(Lista *pocz,Dane x) 

 Lista psz,pop; 
 psz=szukaj(*pocz,x,&pop); 
  if(psz) 
 

 { 

 

  if(pop) 

 

 

pop->nast=psz->nast; 

 

  else 

 

 

*pocz=psz->nast; 

 

  free(psz) 

 

 } 

background image

 78 

 
pocz   pop                  psz 
                   
 
              
          
 
/* kasowanie listy */ 
 
void kasuj_liste(Lista pocz) 
  { 
    

if (pocz != NULL) 

 

  { 

    

   kasuj_liste(pocz -> nast); 

 

   free(pocz); 

    

  } 

  } 
 
 
  n     pn                  po 
                   
 
              
           
 
/* odwracanie listy jednokierunkowej*/ 
 
Lista odwracanie(Lista *pn) 

 Lista po,n; 
 po=NULL;         
 while(*pn) 
 { 
  n=(*pn)->nast; 
  (*pn)->nast=po; 
  po=*pn; 
  *pn=n; 
 } 
return *pn=po; 

 

background image

 79 

16.2.

 

Listy dwukierunkowe 

typedef 

char 

 

Dane; 

 
struct lista_dwa 
  { 
   Dane 

    

d; 

   struct lista_dwa 

*nast,*pop; 

  }; 
 
typedef 

struct lista_dwa  Element2; 

typedef 

Element2 

 

 

*Lista2; 

 

16.2.1.

 

Tworzenie listy 

 

Lista2 napis_na_liste(char s[]) 

 

Lista2 

pocz = NULL, kon; 

 
 

if (*s) 

 

  { 

 

 

pocz = 

(Lista2)malloc(sizeof(Element2)); 
 

 

pocz -> d = *s; 

 

 

pocz->pop=NULL; 

 

 

kon = pocz; 

 

 

for (; *++s ;) 

 

 

 

 

 kon -> nast = (Lista2)malloc 

 

 

 

 

 

 

 

(sizeof(Element2)); 

 

 

 kon->nast->pop=kon; 

 

 

 kon = kon -> nast; 

 

 

 kon -> d = *s; 

 

 

 

 

kon -> nast = NULL; 

 

  } 

 

return pocz; 

background image

 80 

16.2.2.

 

Operacje na listach 

Lista2 koniec(Lista2 pocz) 

 if(pocz) 
  while(pocz->nast) 
 

pocz=pocz->nast; 

 return pocz; 

 
void pisz_n(Lista2 p) 

  printf("\n"); 
 while(p) 
 { 
 

pisz_d(p->d); 

 

p=p->nast; 

 } 

 
Lista2 szukaj(Lista2 pocz,Dane x) 

 while(pocz && pocz->d!=x) 
  pocz=pocz->nast; 
 return pocz; 

Lista2 usun(Lista2 *pocz,Dane x) 

 Lista2 pop,psz=szukaj(*pocz,x); 
  if(psz) 
  { 
 

 pop=psz->pop; 

 

 if(pop) 

 

  pop->nast=psz->nast; 

 

 else 

 

  *pocz=psz->nast; 

 

 if(psz->nast) 

 

  psz->nast->pop=pop; 

 

 free(psz); 

  } 
return *pocz; 

background image

 81 

17.

 

Stos 

 
 

 

szczyt 

----> Dane 

 

 

 

 

 

 

   

 
 

 

 

 

 

 

Dane 

 
 

 

 

 

 

 

Dane 

 

 

 

 

 

 

NULL 

 
Stos jest strukturą typu LIFO (Last In First Out). 
 
Prototypy funkcji: 
int pusty(Stos st);  

 

Wartosc 1, gdy stos pusty; 

 

 

 

 

 

 

0 w przypadku przeciwnym/ 

Dane wart(Stos st);   

 

Wartość elementu na szczycie stosu. 

void pop(Stos *st, Dane *d); 

Usuwa element ze szczytu stosu 

 

 

 

 

 

 

i daje jesgo wartość. 

void push(Stos *st, Dane d); 

Umiescza wartosc d na stosie. 

 
 
Plik stos.h: 
 

#define  

NULL 

 

typedef 

char 

 

Dane; 

 
struct stos 
  { 
   Dane 

    

d; 

   struct stos *nast; 
  }; 
 
typedef 

struct stos 

Element; 

typedef 

Element 

 

*Stos; 

 
 

background image

 82 

/* operacje na stosach */ 
int pusty(Stos st) 
  { 
 

return (st == NULL); 

  } 
Dane wart(Stos st) 
 { 
   return (st -> d); 
 } 
void pop(Stos *st, Dane *d) 
  { 
   Stos st1 = *st; 
 

if (!pusty(st1)) 

 

  { 

      *d = st1 -> d; 
 

   *st = st1 -> nast; 

 

   free(st1); 

       } 
 

else 

 

  printf("Stos jest pusty\n"); 

  } 
void push(Stos *st, Dane d) 
  { 
   Stos rob; 
 

rob = malloc(sizeof(Element)); 

 

rob -> d  = d; 

 

rob -> nast = *st; 

 

*st = rob; 

  } 
/* odwracanie napisu */ 
void odwroc(Dane s[]) 
  { 
   int i; 
   Stos st = NULL; 
 

for (i = 0; s[i] != '\0'; ++i) 

 

  push(&st,s[i]); 

 

for (i = 0; s[i] != '\0'; ++i) 

 

  pop(&st, &s[i]); 

  } 

background image

 83 

17.1.

 

Stos i odwrotna notacja polska 

17.1.1.

 

Notacja polska  

Notacja ta bywa zwana też - od nazwiska jej twórcy- 

NOTACJĄ 

Ł

UKASIEWICZA 

 

 
Notacja infiksowa  

Notacja polska  Odwrotna notacja polska 

  x+y  

+ x y 

x y + 

2*(2-4)/5 

/ *2 -2 4 5 

2 2 4 - * 5 / 

 
Cechy notacji polskiej 


 

umożliwia zapis dowolnego wyrażenia bez konieczności użycia 
nawiasów 



 

ułatwia kontrole poprawności wyrażenia 



 

jest to notacja często stosowana w wiekszości kolkulatorów 

 


 

wymusza konieczność rozróżnienia operatorów binarnych i unarnych 
(np. + - ) 



 

skomplikowany zapis 

 

17.1.2.

 

Zamiana notacji infiksowej na odwrotną notacje polska 

Potrzebne są nam trzy stosy. 

••••

 

Stos wejściowy  

(WE)  

••••

 

Stos wynikowy  

(WY) 

••••

 

Stos pomocniczy   (POM) 

Założmy, że na stosie WE dane mamy wyrażenie rozbite na atomy i 
ułożone w takiej kolejmości aby pobieranie z niego danych odpowiadało 
kolejności pojawiania się atomów w wyrażeniu. Mogła by tu być użyta 
kolejka. 
Np. 2*(3+7) 

1)

 

Przetwarzamy wyrazenie pojawiające na stosie WE 

2)

 

Jeśli atom jest: 
a)

 

Liczbą 

to „wrzycamy” ja na stos (WY) 

b)

 

Nawiasem „(”  to „wrzycamy” ja na stos (POM) 

c)

 

Nawiasem „)”  to ze stos  (POM) zdejmujemy operator i kładziemy 

go na stos (WY) 
operacje tą powtarzamy az do momentu zdjęcia ze 
stosu (POM)  atomu „)”-tego atomu nie kładziemu na 
stosie 

background image

 84 

d)

 

Operatorem O  sprawdzamy jaki operator jest na stosie (POM) 

-jeśli stos jest pusty to kładziemy operator O na stos 
(POM) 
-jeśli operator na stosie ma wyższy priorytet to 
kładziemy go z powrotem na stos (POM) i nastepnie 
dokładamy operator O 
- jeśli operator na stosie ma niższy bądź równy 
priorytet to kładziemy na stos (WY)  
 nastepnie powtarzamy operację dla kolejnego 
poeratora na stosie (POM) 
na koniec kładziemy operator na sios (POM) 

3)

 

Gdy stos (WE) jest już pusty to przekładamy wszystkie pozostałe 
operatory ze stosu (POM) na stos (WY) 

4)

 

Otrzymujemy stos (WY) z wyrażeniem w odwrotnej notacji polskiej, 
ale od jego końca westarczy go tyklo odwrócić 

5)

 

Zamiast punktu 3) można przenieść dane ze stosu (WY) na stos (POM). 
Wtedy wynikiem jest stos (POM). 
 

17.1.3.

 

Kalkulator stosowy 

 
Potrzebne są nam dwa stosy. 

••••

 

Stos wejściowy  

(WE)  

••••

 

Stos wynikowy  

(WY) 

Założmy, że na stosie WE dane mamy wyrażenie w odwrotnej notacji 
polskiej rozbite na atomy. 
 
1)

 

Przetwarzamy wyrazenie pojawiające na stosie WE 

2)

 

Jeśli atom jest: 
a)

 

Liczbą 

to „wrzycamy” ja na stos (WY) 

b)

 

Operatorem O  to ze stosu (WY) pobiaramy arg2 

następnie ze stosu (WY) pobiaramy arg1 
i na stos (WY) wkładamy wynik wyrażenia 
arg1 O arg2  (zapis w notacji infiksowej) 
 

3)

 

Wynikiem jest liczba na stosie WE 

4)

 

Jeśli na stosie WE zabraknie argumentów, bądź jeśli zostanie więcej niż 
jedna liczba, oznacze to sytuację błędną. 
Możliwa jest zatem kontrola poprawności wprowadzonego wyrażenia.   

Przykład: 

background image

 85 

 3- (4*5)*(6+7)/8  = -29.5 

1.

 

Zamiana na odwrotną notację polską 

1.

 

3 - ( 4 * 5 ) * ( 6 +  7 ) /8   WE 

          

  POM 

 

  WY 

 
2.

 

 - ( 4 * 5 ) * ( 6 +  7  ) /8    WE 

          

  POM 

 

3  WY  

 
3.

 

 ( 4 * 5 ) * ( 6 +  7  ) /8    WE 

          

-  POM 

 

3  WY 

 
4.

 

 4 * 5 ) * ( 6 +  7  ) / 8 

  WE 

          

( -  POM 

 

3  WY 

 
5.

 

5 ) * ( 6 +  7  ) / 8 

  WE 

          

* ( -  POM 

 

4 3  WY 

 
6.

 

 ) * ( 6 +  7  ) / 8 

  WE 

          

* ( -  POM 

 

 5 4 3  WY 

 
7.

 

 * ( 6 +  7  ) / 8 

  WE 

          

 -  POM 

 

  * 5 4 3  WY 

 
8.

 

 ( 6 +  7  ) / 8 

  WE 

          

* -  POM 

 

  * 5 4 3  WY 

 
9.

 

6 +  7  ) / 8 

  WE 

          

 ( * -  POM 

 

  * 5 4 3  WY 

10.

 

   +  7  ) / 8 

  WE 

          

 ( * -  POM 

 

  6 * 5 4 3  WY 

 
11.

 

    7  ) / 8 

  WE 

          

+  ( * -  POM 

 

  6 * 5 4 3  WY 

 
12.

 

     ) / 8 

  WE 

          

+  ( * -  POM 

 

7  6 * 5 4 3  WY 

 
13.

 

     / 8 

  WE 

          

   * -  POM 

 

+ 7  6 * 5 4 3  WY 

 
14.

 

   8 

  WE 

          

   / -  POM 

 

* + 7  6 * 5 4 3  WY 

 
15.

 

    

  WE 

          

   / -  POM 

 

8 * + 7  6 * 5 4 3  WY 

 
16.

 

A  

  WE 

          

  POM 

 

 - / 8 * + 7  6 * 5 4 3  WY 

 
16.

 

B  

  WE 

            3 4 5 * 6 7 + * 8  / -  POM 
 

  WY 

 

background image

 86 

2. Wyliczenie wartości wyrażenia 
 
1.

 

  

3 4 5 * 6 7 + * 8  / -   WE 

  

  WY 

 
2.

 

 

 4 5 * 6 7 + * 8  / -   WE 

  

3  WY 

 
3.

 

 

  5 * 6 7 + * 8  / -   WE 

  

4 3  WY 

 
 
4.

 

 

   * 6 7 + * 8  / -   WE 

  

5 4 3  WY 

 
5.

 

 

    6 7 + * 8  / -   WE 

  

20 3  WY 

 
6.

 

 

     7 + * 8  / -   WE 

  

6 20 3  WY 

 
7.

 

 

      + * 8  / -   WE 

  

7 6 20 3  WY 

 
8.

 

 

       * 8  / -   WE 

  

13 20 3  WY 

 
9.

 

 

        8  / -   WE 

  

260 3  WY 

 
10.

 

 

          / -   WE 

  

 8 260 3  WY 

 
11.

 

  

   -   WE 

  

 32.5 3  WY 

 
12.

 

 

  WE 

  

 -29.5   WY 

background image

 87 

18.

 

Drzewa binarne 

 
 

Drzewo jest skończonym zbiorem elementów zwanych węzłami. 

Drzewo posiada jeden wyróżniony element zwany korzeniem, pozostałe 
węzły tworzą rozłączne poddrzewa. Jeżeli węzeł r ma poddrzewa T

1

, T

2

..., T

n

, to korzenie tych drzew są potomkami węzła r. Węzeł, który nie ma 

potomków nazywamy liściem. 
 
 

 

 

 

 

drzewo 

 

 

 

 

 

  a 

 

 

 

kkorzeń 

 
poddrzewo 
 
 

 

 

 

  f 

 

 

 

 
 
 

 

  h                  liście 

 
 
Drzewami binarnymi nazywamy drzew, których węzły mają dwóch 
potomków. 
 
 
Plik drzewo.h: 
 

#define 

 

NULL 

 

 
typedef 

 

char 

 

Dane; 

 
struct wezel 
 

 

 Dane         d; 

 

 struct wezel *lewy; 

 

 struct wezel *prawy;      

 

}; 

 
typedef struct wezel WEZEL; 
 
typedef WEZEL   

*DRZEWO; 

background image

 88 

18.1.

 

Przeglądanie drzew binarnych 

Kierunek: 

 

LKP  

KLP  

LPK  

 

 

 

Lewe 

Korzeń 

Lewe 

 

 

 

Korzeń 

Lewe 

Prawe 

 

 

 

 

Prawe 

Prawe 

Korzeń 

 

void lkp(DRZEWO korzen) 
  { 
 

if (korzen != NULL) 

 

  { 

 

 

lkp(korzen -> lewy); 

 

 

printf("%c ", korzen -> d); 

 

 

lpk(korzen -> prawy); 

 

  } 

  } 

 
 

 

 

 

 

 
 
 

 

 

 

 

 

 

 
 
 

 

 

 

 

 

 
 
A   

C   

 
lpk :   A B C D E F G H I J 
 

void  klp(DRZEWO korzen) 
  { 
 

if (korzen != NULL) 

 

  { 

 

 

printf("%c ", korzen -> d); 

 

 

lkp(korzen -> lewy); 

 

 

lpk(korzen -> prawy); 

 

  } 

  } 

                   
klp:  

G D B A C F E I H J 

background image

 89 

18.2.

 

Tworzenie drzewa binarnego 

 

DRZEWO nowy_wezel() 
 

 

 

return (malloc(sizeof(WEZEL))); 

 

 
DRZEWO inic_wezel(Dane d1, DRZEWO p1, DRZEWO p2) 
  { 
 

DRZEWO t: 

 
 

t = nowy_wezel(); 

 

t -> d = d1; 

 

t -> lewy = p1; 

 

t -> prawy = p2; 

 

return t; 

  } 
 

Tworzenie drzew binarnego z tablicy: 
potomkami a[i] są: a[2*i+1] i a[2*i+2] 
 

DRZEWO tw_drzewo(Dane a[], int i, int ilosc) 
  { 
 

if (i >= ilosc) 

 

  return NULL; 

 

else 

 

  return (inic_wezel(a[i], 

 

 

 

 

 tw_drzewo(a, 2*i+1, ilosc), 

 

 

 

 

 tw_drzewo{a, 2*i+2, ilosc)); 

  } 
 
 

background image

 90 

18.3.

 

Drzewa wielokierunkowe 

 
Drzewo wielokierunkowe -  
 

węzły mają dowolną liczbę potomków. 

 
Reprezentacja: 
 

Lista liniowa dla każdego węzła. 

 

Każda lista jest potomkiem jednego węzła. 

 

Tablica pierwszych potomków. 

 
 
 

 

t[0]  

 

 

 
 
 

 

 

 

 

 

 

 

 

 
 
 

 

 

d  

 

 

 

NULL 

 
 

 

NULL 

  NULL 

NULL     NULL 

 
 
Plik w_drzewo.h: 
 

#define 

 

NULL  

typedef 

 

char 

 

Dane; 

 
struct wezel 
  { 
 

int 

 

 

nr_pot; 

 

Dane 

 

 

d; 

 

struct wezel 

*r; 

  }; 
 
typedef struct wezel WEZEL; 
typedef WEZEL  *W_DRZEWO; 

 

background image

 91 

Tworzenie węzła: 

W_DRZEWO nowy_wezel() 
  { 
    

return(malloc(sizeof{WEZEL))); 

  } 
W_DRZEWO inic_wezel(Dane d1, int num, W_DRZEWO rd) 
  { 
   W_DRZEWO rob: 
 
   rob = nowy(wezel); 
 

rob -> d = d1; 

 

rob -> nr_pot = num; 

 

rob -> r = rd; 

 

return rob; 

  } 
t[0] = inic_wezel('a', 1, NULL); 
t[1] = inic_wezel('b', 2, NULL); 
t[1] -> r = inic_wezel('f', 6, NULL); 
t[1] -> r -> r = inic_wezel('g', 7, NULL); 
t[2] = inic_wezel('c', 3, NULL); 
t[2] -> r = inic_wezel('d', 4, NULL); 
t[2] -> r -> r = inic_wezel('e', 5, NULL); 
t[3] = t[4] t[5] = NULL; 
t[6]  = inic_wezel('h', 8, NULL); 
t[7] = t[8] = NULL; 

18.4.

 

Przeglądanie drzewa wielokierunkowego 

 

void przeg(W_DRZEWO t, int ind) 
  { 
   W_DRZEWO rob; 
 
 

rob = t[ind]; 

 

while (rob != NULL) 

     { 
 

   printf("%c  %d\n", rob->d, rob->nr_pot); 

 

   przeg(t, rob->nr_pot); 

 

   rob = rob->r; 

       } 
  } 

background image

 92 

19.

 

Preprocesor 

19.1.

 

Sklejanie wierszy 

Wiersze zakończone znakiem   \   są sklejane z następnym wierszem. 
Nastepuje to przed podziałem pliku na leksemy. 

int nazwa\ 
zmiennej; 

Definicja zmiennej 

nazwazmiennej 

 

Derektywy preprocesora dotyczą jednej lini kodu, jeśli dyrektywa jest dość 
długa to powyższa metoda może być użyteczna. 
 

19.2.

 

Dyrektywa #include 

 
#include 

#include  

 
Przykład. 
 

Plik prog.h: 

 

 

#include <stdio.h> 

 

 

#include <stdlib.h> 

 

 

. . . 

 

Plig prog.c: 

 

 

#include "prog.h" 

 
 

19.3.

 

Dyrektywa #define 

 

 

#define identyfikator wartość 

 

#define  

ILOSC_SEKUND_W_DOBIE  \ 

            (60 * 60 * 24) 
 
#define  

PI  

 

3.141592 

#define 

EOF 

 

 

(-1) 

#define  

MAXINT  

2147483647 

 

Przykład. 

background image

 93 

 

#define  EQ == 

 

 

 

#define  do 

 
 

while ( i EQ 1 ) do 

 

  { 

        . . . 
 
 

while ( i == 1) 

 

  { 

    

 

. . . 

19.3.1.

 

Użycie makrdefinicji z parametrami 

 

#define nazwa(nazwa, ..., nazwa) 

wartość 

 
#define 

SQ(x)  ((x)  * (x)) 

 
 

SQ(7 + w) 

 

 

 

 

((7 + w) * (7 + w)) 

 
 

SQ(SQ(*p)) 

 

 

 

 

 

((((*p) * (*P)) * (((*p) * (*p)))) 

 

ale 

 
#define  

SQ(x)  x * x 

 
 

SQ(a+b) 

 

 

 

 

 

a + b * a + b 

 
 

 

zamiast 

 

 

((a + b) * (a + b)) 

Uwagi: 

1. 
 

#define  SQ(x)  (x) * (x) 

 

background image

 94 

 

 

4 / SQ(2) 

 

 

 

 

 

 

4 / (2) * (2) 

 

 

 

 

zamiast 

 

 

 

4 / ((2) * (2)) 

 

2.  

#define  SQ (x) ((x) * (x)) 

 
 

 

SQ(7) 

 
 

 

 

(x) ((x) *(x)) (7) 

 
3. #define  SQ(x)  ((x) * (x)) ;  
 
 

 

x = SQ(x); 

 

 

 

 

 

 

x = ((x) * (x)) ;; 

 

 

ale 

 

 

if (x == 2) 

 

 

 

x = SQ(y); 

 

 

else 

 

 

 

++x; 

Przykłady. 
 

1. #define  min(x,y) 

(((x) < (y)) ? (x) : (y)) 

 
 

 

m = min(u,v); 

 
 

 

 

m = (((u) < (v)) ? (u) : (v)); 

 
2. #define  min4(a,b,c,d)  min(min(a,b), min(c,d)) 
 
3. #define  SQ(x)  ((x) * (x)) 
 

#define  CUBE(x) 

(SQ(x) * (x))  /* x ^ 3 */ 

 

#define  P_4_3  sqrt(sqrt(CUBE(x))) 

PRZYKŁAD 

#include 

<stdio.h> 

#include 

<stdlib.h> 

 

 

background image

 95 

#define 

M  32  /* wymiar a[] */ 

#define   N 11 /* wymiar b[] */ 
 
#define 

czesc_ulamkowa(x) (x - (int) x) 

#define 

random_char()   

(rand() % 26 + 'a') 

#define 

random_float() 

 

(rand() % 100 / 10.0) 

 
#define INIC(a,c,typ) \ 
  for (i = 0; i < n; ++i) \ 
   a[i] = (*typ == 'c') ? random_char() : \ 
 

random_float() 

 
#define DRUKUJ(a, n, zn_ster)   

 

 

for (i = 0; i < n; ++i) 

 

 

  printf(zn_ster, a[i]);  \ 

 

 

putchar('\n') 

main() 
  { 
   char a[M]; 
   float b[N]; 
   int i; 
   int lex(void *, void *); 
   int por_cz_ul(void *, void *); 
 

INIC(a, M, "char"); 

 

DRUKUJ(a, M, "%-2c"); 

 

qsort(a, M, siceof(char), lex); 

 

DRUKUJ(a, M, "%-2c"); 

 

printf("--------------\n"); 

     INIC(b, N, "float"); 
 

DRUKUJ(b, N, "%-6.1f"); 

 

qsort(b, N, siceof(float), por_cz_ul); 

 

DRUKUJ(b, N, "%-6.1f"); 

  } 

background image

 96 

int lex(void *vp, void *vq) 
  { 
 

char *p = vp, *q = vq; 

 

return (*p - *q); 

  } 
 
int por_cz_ul(void *vp, void *vq) 
  { 
   float *p = vp, *q = vq, x; 
 
 

x = czesc_ulamkowa(*p) - czesc_ulamkowa(*q); 

 

return ((x == 0.0) ? 0 : (x < 0.0 ? -1 : +1)); 

  } 
 

19.4.

 

Dyrektywa #undef 

 

 

#undef nazwa 

odwołanie definicji makroinstrukcji  
 

 
#ifdef max 
 #undef max 
#endif 
#define max (x,y)       (((x) > (y)) ? (x) : (y)) 

background image

 97 

 

19.5.

 

Kompilacja warunkowa 

wiersz-if tekst częsci-elif część-else

opc

 #endif 

 
wiersz-if 

#if wyrażenie stałe 
#ifdef identyfikator 
#ifndef identyfikator 

 
cz
ęści-elif 

wiersz –elif tekst 
części-elif

opc

 

 
wiersz-elif 
#elif wyrażenie-stałe 
 
cz
ęść-else 
wiesz-else 
tekst 
wiesz-else 
#else 
 

wyrażenie-stałe  
defined(identyfikator) 
 !defined(identyfikator) 
 
 
Przykład: 

#define __WERSJA_1_0 
 printf( 
 

 

#ifdef __WERSJA_1_0 

 

 

 " Wersja 1.0" 

 

 

#elif defined(__WERSJA_2_0) 

 

 

 " Wersja 2.0" 

 
 

 

#else __DEMO__ 

 

 

 " Wersja DEMONSTRACYJNA" 

 

 

#endif 

 

 

); 

 

background image

 98 

Przykład: 
Warunkowe dołączanie plików nagłówkowych: 
 

#ifndef _ _napisy_h_ _ 

  #define _ _napisy_h_ _ 

 
import int  slen(const char *s);   

 /*dlugosc napisu*/ 

char* scopy(char *cel,const char * zrodlo);  

/* kopiowanie*/ 

char* sncopy(char *cel,const char * 

zrodlo,int n); 
/*kopiowanie co najwyzej n znakow*/ 

char* scat(char *cel,const char * zrodlo);  

/*laczenie*/ 

int scomp(const char *s1,const char * s2); 

/*porownanie*/ 

int sicomp(const char *s1,const char * s2); 

/*porownanie z ignorowaniem wilekosci liter*/ 

char* srev(char *p,const char *s);                  

/* odwracanie*/ 

#endif 

background image

 99 

 

19.6.

 

Inne dyrektywy 

#define 

#undef 

#include 

19.6.1.1.

 

#   

Pusta instrukcja preprocesora   -nie ma żadnych skutków 

19.6.1.2.

 

# pragma derektywa 

Wykonuje dyrektywe specyficzną dla kompilatora. Jeśli kompilator nie 
rozpoznaje dyrektywy to ją ignoruje bez zgłoszenie błędu czy nawet 
ostrzeżena. 
 

void koniec() 

printf("Koniec"); 

 
#pragma exit koniec 

19.6.1.3.

 

# error tekst 

Powoduje, że w trakcie kompilacji wypiany zostanie tekst 

 
#error To jest blad kompilacji 

19.6.1.4.

 

# line nr_lini    ‘’plik’’ 

Dyrektywa powoduje, że dla celów diagnostycznych zakłaga się, że 
następną linią jest linia nr_lini w pliku plik. Jeśli ''plik'' zostanie 
opuszczony to za plik uznaje się plik bieżący. 
 
Dyrektywa używana dla innych kompilatorów generujących kod w języku 
C. 

19.7.

 

Makrorozwinięcia w stdio.h i ctype.h 

 
 

#define  

getchar()   

getc(stdin) 

 

#define 

putchar(c) 

putc((c), stdout) 

 

isalpha(c) 

 

isupper(c) 

 

islower(c) 

 

. . . 

background image

 100 

19.8.

 

Nazwy zdefiniowane w proprocesorze 

 
_ _ LINE _ _  

numer wiersza pliku źródłowego 

_ _ FILE _ _ 

nazwa pliku źródłowego 

_ _DATE _ _ 

data tłumaczenia programy ''Mmm dd rrr'' 

_ _ TIME_ _ 

czas tłumaczenia programy ''gg:mm:ss'' 

_ _ STDC_ _ 

stała 1- ma to oznaczać zgodność ze standardem ANSI 

 
Definicji tych nie można zmieniać. 
 
 
#include<stdio.h> 
#ifndef __STDC__ 
 #include <conio.h> 
 
#else 
 #define cprintf printf 
 
#endif 
 main() 

#ifndef __STDC__ 
 textcolor(YELLOW); 
#endif 
cprintf("Koplilacja za pomocą %s\n\r", 
 

 

#ifndef __STDC__ 

 

 

 

"BORLAND C++" 

 

 

#else 

 

 

 

"ANSI C" 

 

 

#endif 

); 
 
#ifndef __STDC__ 
 textcolor(LIGHTGRAY); 
#endif 
 cprintf("Kod Źródłowy %s \n\rData kompilacji %s %s \n\rIlość lini %d", 
 

 __FILE__,__DATE__, __TIME__,__LINE__+1); 

return; 

background image

 101 

19.9.

 

Tworzenie aplikacji wielomodułowych  

19.9.1.

 

Projekty 

 
Zalety i wady projektów: 

••••

 

Podział programu na moduły 

••••

 

Możliwość korzystania z gotowych bibliotek (LIB) 

••••

 

Zapamiętanie układu scieżek  

background image

 102 

19.9.2.

 

Tworzenie bibliotek 

 
 

Tworzenie bibliotek umożliwia udostępnianie funkcji bibliotecznych z 
jednoczesnym ukruciem ich kodu. 

background image

 103 

19.9.3.

 

Udostępnianie danych 

W każdym module przynajmniej jedna funkcja musi być publiczna 
 
 Wskazane jest do każdego modułu ( konieczne gdy uzywamy modułów 
już wcześniej skompilowanych) dołączenie pliku nagłówkowego 
 

19.9.4.

 

 Ukrywanie danych 

Jest to klasyczne podejście w programowaniu obiektowym, ale istnieje od 
dawna w języku C. 
Widoczność  zmniennej zadeklarowana jako 

static

 jest ograniczona 

tylko do pliku, w którym występuje je definicja. 

 
static int PrivateCounter; 
 
/* Funkcje publiczna */ 
int DecCounter() 

  if(PrivateCounter) 
 return --PrivateCounter; 

 
int IncCounter() 

  if(PrivateCounter<CounterLimit) 
 return ++PrivateCounter; 

19.9.5.

 

Niepełne definicje typów 

Ukrywać dane można także poprzez nieudostępnianie pełnej struktury 
danych. 

typedef struct StacTag STACK; 
 
STACK *Creat(int size); 
int pop(STACK *Stack); 
int push(STACK *Stack,int Value); 
int empty(STACK *Stack); 

Programista może posługiwać się strukturą tylko w ograniczonym 
kontekście-gdy nie jest potrzebna jej wielkość , stosując np. wskaźnik do 
struktury 

background image

 104 

Kompilator w momencie definicji zakłada, że pełna definicja struktury 
pojawi się później 
 

typedef struct StackTag 

 int *Ptr;/* miejsce na dane */ 
 int Size; 
 int Current; 

 

Kompilator , ponieważ struktura 

StackTag

 nie będzie znana w innych 

modułach będzie generował odpowiednie ostrzeżenia, ale obsługa tego 
typu będzie poprawna. 
Takie podejście uniemożliwia bezpośredni dostęp do pól sturuktury - 
nawet jeśli ich nazwy będą znane programiście. 

STACK * s; 
... 
s->Counter++/* bł

ą

d kompilacji*/ 

 

19.9.6.

 

Funkcje prywatne 

 
Funkcje także można uczynić prywatnymi: 

static int FunkcjaPomocnicza(); 
 

Zasady widoczności funkcji prywatnych są identycze jak przy zmiennych. 
 
Łatwo znaleźć wiele przykładów gdzie zarówno dane jak i kod należy 
chronić przed ich niepowołanym dostępem. Może to dotyczyc również 
dostępu przez samego autora. 

 
 

background image

 105 

20.

 

Funkcje ze zmienną ilością argumentów 

 
Niezbędne definicje znajdują się w pliku: 

#include <stdarg.h> 

typ   

va_list 

typ umozliwiający dostęp do 
argumentów

 

makro

 

va_start 

inicjacja zmiennej typu 

va_list 

na

 

pierwszy argument z listy  
Nie zwraca wartości. 

void va_start(va_list ap, lastfix); 
 
makro

 

va_arg 

indeksowanie listy zmiennych 
argumentów

 

type va_arg(va_list ap, type); 
 
makro

 

va_end 

polwala funkcji na poprawne 
zakończenie. Nie zwraca wartości. 

void va_end(va_list ap); 
Przykład: 

#include <stdio.h> 
#include <stdarg.h> 
void sum(char *msg1,char* msg2, ...) 

        int total = 0; 
        va_list ap; 
        int arg; 
        va_start(ap, msg2); 
        printf("\n%s ",msg1); 
        while ((arg = va_arg(ap,int)) != 0) { 
                total += arg; 
                printf("%d +",arg); 
        } 
        printf("\b %s %d",msg2, total); 
        va_end(ap); 

int main(void) { 
        sum("Suma","wynosi", 1,2,3,4,0); 
        return 0; 

background image

 106 

#include <stdarg.h> 
#include <ctype.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
int print( const char *format,...); 
 
int main() 

 float f=3.5; 
 print(" %f %5c %c %d\n",f+1.3,'

ů

','b',10); 

 print("\t%3d+%d=%4d \n%f\tok",12,3,12+3,f); 
return 0; 

 
int print( const char *format,...) 

 

va_list ap; 

 

int arg,count=0,precision=3,wide=0; 

 

char FillChar='.'; 

 

const char *FFF=format; 

 

va_start(ap, format); 

 
 

while(*format){ 

 

int wproc=1; 

 
 

switch(*format) 

 

 

case '%': 

 

do{ // do przetwarzania formatu 

 

switch(*++format) 

 

 

{   // szeroko

ść

 pola i 

 

case '0':case '1':case '2':case '3':case '4': 
case '5': case '6': case '7':case '8': case '9': 

 

 

 

 

char Wide[10]="",cw=0; 

 

 

while(isdigit(*format)) 

 

 

Wide[cw++]=*format++; 

 

 

Wide[cw]=0; 

 

 

wide=atoi(Wide); 

 

 

if(*format=='.') 

background image

 107 

 

 

 

 

char Prec[10]="",cp=0; 

 

 

++format; 

 

 

while(isdigit(*format)) 

 

 

 

Prec[cp++]=*format++; 

 

 

Prec[cp]=0; 

 

 

precision=atoi(Prec); 

 

 

 

 

--format; 

 

 

}continue; 

 

 

case 'd':{// liczba int 

 

 

 

int sl,arg=va_arg(ap,int); 

 

 

 

char *a="..........."; 

 

 

 

itoa(arg,a,10); 

 

 

 

sl=strlen(a); 

 

 

while(wide>sl++)putchar(FillChar); 

 

 

 

while(*a) 

 

 

 

putchar(*(a++)); 

 

 

 

count++; 

 

 

 

 

 

break; 

 

 

case 'f':{ 

 

 

// liczba double (float) 

 

 

 

int sl; 

 

 

double arg=va_arg(ap,double); 

 

 

 

int dec,sign; 

 

char *a=fcvt(arg,precision,&dec,&sign); 

 

 

 

sl=strlen(a); 

 

 

 

if(dec<sl)sl++; 

 

 

 

if(sign)sl++; 

 

 

 

while(wide>sl++) 

 

 

 

putchar(FillChar); 

 

 

 

if(sign) 

 

 

 

putchar('-'); 

 

 

 

while(dec--&&*a) 

 

 

 

putchar(*(a++)); 

 

 

 

putchar('.'); 

 

 

 

while(*a) 

 

 

 

putchar(*(a++)); 

 

 

 

count++; 

 

 

 

background image

 108 

 

 

break; 

 

 

case 'c':{ 

 

 

 

char arg=va_arg(ap,char); 

 

 

while(--wide>0) putchar(FillChar); 

 

 

 

putchar(arg); 

 

 

 

count++; 

 

 

 

 

 

 

break; 

 

 

default:putchar(*format); 

 

 

 

wproc=0;// wyj

ś

cie z p

ę

tli 

 

}while(wproc); 

 

case '\\': switch(*++format) 

 

 

 

 

case 'n':putchar('\n');break; 

 

 

case 'r':putchar('\r');break; 

 

 

case 't':putchar('\t');break; 

 

 

default:putchar(*format); 

 

 

 

 

break; 

 

default: putchar(*format);break; 

 

 

*++format; 

 

precision=3;wide=0; 

 

} ; 

return count; 

background image

 109 

20.1.

 

Funkcje biblioteczne 

int printf(const char *format[, argument, ...]); 
int sprintf(char *buffer, const char *format[, argument, ...]); 
int fprintf(FILE *stream, const char *format[, argument, ...]); 
int vprintf(const char *format, va_list arglist); 
int vfprintf(FILE *stream, const char *format, va_list arglist); 
int vsprintf(char *buffer, const char *format, va_list arglist); 
 
 
Przykład: 

#include <stdio.h> 
#include <stdarg.h> 
 
int vpf(char *fmt, ...) 

   va_list argptr; 
   int cnt; 
 
   va_start(argptr, fmt); 
   cnt = vprintf(fmt, argptr); 
   va_end(argptr); 
 
   return(cnt); 

 
int main(void) 

   int inumber = 30; 
   float fnumber = 90.0; 
   char *string = "abc"; 
 
   vpf("%d %f %s\n",inumber,fnumber,string); 
 
   return 0; 

 

background image

 110 

21.

 

Wejście/wyjście 

21.1.

 

Standardowa funkcja wejścia 

int printf(const char *format[, argument, ...]); 
 

 

 - 

dosuniecie do lewej strony 

 

 d 

 

liczba dziesietna 

 

 x 

l. szesnastkowa 

 

 o 

l. ósemkowa 

 

 f 

l. float lub double 

 

 e 

 

liczba float lub double w zapisie naukowym 

 

 g

 

jeden z d f e (o najmniejszym polu) 

 

 s

 

łańcuch znzków 

 

 p

 

wskaźnik 

 

 u

 

liczba bez znaku 

 

 c

 

zank 

 

 %

 

znak % 

 

 .

 

dzieli szerokość pola od precyzji 

 

 h

 

liczba krótka 

 

 l

 

liczba długa 

 

 * 

 

umożliwia pobranie szerokości pola  

21.2.

 

Standardowa funkcja wejścia 

 
 int scanf(const char *format[, address, ...]); 
 

 

ld 

liczba typu long 

 

lf 

liczba typy double 

 

Lf 

liczbatypu long double 

 

 [] 

wczytuje tylko znaki ujęte w klamry 

 

 [^] 

znak w klamrze jest końcem pobierania łancucha 

 

 * 

ignoruje pobrany element  

 
 

background image

 111 

#include <stdio.h> 
int main(){ 
 char imie[10],nazw[21],adres[31],tel[16]; 
 printf( 
"wizytówka\npodaj swoje dane\n\timie(imiona)\t"); 
 

scanf("%*[\n]%10[^\n]%*[^\n]",imie); 

 

printf("\tnazwisko\t"); 

 

scanf("%*[\n]%20[^\n]%*[^\n]",nazw); 

 

printf("\tadres\t\t"); 

 

scanf("%*[\n]%30[^\n]%*[^\n]",adres); 

 

printf("\ttelefon\t\t"); 

 

scanf("%*[\n]%15[^\n]%*[^\n]%*c",tel); 

 

printf("%s %s \n%s\ntel. %s " ,  

 

 

 

 

 

imie,nazw,adres,tel); 

return; 

Przykład 2: 

#include <string.h> 
#include <stdio.h> 
int main(){ 
 char imie[256]; 
 FILE *f=fopen("file1.c","r"); 
 while(1) { 
 if((fscanf(f,"%256[a-zA-z_]",imie))==EOF)break;  
 

 printf("%s ",imie); 

 if((fscanf(f,"%256[^a-zA-z_]",imie))==EOF)break; 

 

printf("%s ",imie); 
 } 
fclose(f); 
return; 

Przykład 3: 

#include<stdio.h> 
int main(){ 
int i,d;  unsigned u; char c,s[10]; float f; 
printf(" podaj znak ascii, liczbe calkowita , l. \ 
 

nieujemna, l.rzeczywista napis l. calkowita"); 

 scanf("%c %d %u %f %9s%* %x",&c,&i,&u,&f,s,&d); 
 printf("%d %u %c %g %9s %x",i,u,c,f,s,d); 
return; 

background image

 112 

21.3.

 

Napisowe funkcje wejścia/wyjścia. 

 
int sprintf(char *s, const char *format, ... ); 
int sscanf(char *s, const char *format, ... ); 
 
Przykład. 
 

char str1[] = "3 4 start", str2[100], rob[100]; 
int a,b; 
 
sscanf(str1, "%d%d%s", &a, &b, rob); 
sprintf(str2, "%s %s %d %d\n", rob, rob, a, b); 
printf("%s", str2); 

 

21.4.

 

Plikowe funkcje IO 

 
Pliki standardowe: 

  stdin  

wejscie standardowe 

klawiatura 

  stdout 

wyjście standardowe 

 

ekran 

  stderr 

standardowy plik błędów 

ekran 

 
inne pliki deklarujemy używając FILE zdefiniowanego w stdio.h. 
 
int fprintf(FILE *fp, const char *format, ... ); 
int fscanf(FILE *fp, const char *format, ...); 
 
 

fprintf(stdout, ... )  

 

 

 

printf( . . .) 

 

fscanf(stdin, ...)  

 

 

 

scanf( . . .) 

 
Uwaga. 
 

fprintf(stderr   - działa przy zmianie wyjscia) 

background image

 113 

21.5.

 

FUNKCJE fopen() i fclose() 

#include <stdio.h> 
 

FILE  fopen(char  *nazwa_pliku, char  *tryb); 

 
 

 

tryb  

 

 

znaczenie 

 

 

------------------------------------- 

 

 

"r"   

 

 

plik tekstowy - czytanie 

 

 

"w"  

 

 

plik tekstowy - pisanie 

 

 

"a"   

 

 

plik tekstowy - dołączanie 

 

 

"r+"  

 

 

plik tekstowy - czytanie i pisanie 

 

 

"rb"  

 

 

plik binarny - czytanie 

 

 

"wb" 

 

 

plik binarny - pisanie 

 

 

"ab"  

 

 

plik binarny - dołącznie 

 

 

"wb+" 

 

 

plik binarny - pisanie i czytanie 

 
int fclose(FILE *stream); 
 
Przykład. 

#include  

<stdio.h> 

#include 

<stdlib.h> 

 
void podw_odstp(FILE *, FILE *); 
void prn_info(char *); 
 
main (int argc, char **argv) 
  { 
   FILE *ifp, *ofp; 
 
 

if (argc != 3) 

 

  { 

 

 

prn_info(argv[0]); 

 

 

exit(1); 

 

  } 

ifp = fopen(argv[1], "r");    /* plik wej

ś

ciowy */ 

ofp = fopen(argv[2], "w");  

/* plik wynikowy */ 

 

podw_odstp(ifp, ofp); 

 

fclose(ifp); 

 

fclose(ofp); 

  } 
void podw_odstp(FILE *ifp, FILE *ofp) 

background image

 114 

  { 
   int c; 
 
 

while ((c = getc(ifp)) != EOF) 

 

  { 

      putc(c, ofp); 
 

   if (c == '\n') 

 

 

putc('\n', ofp);  /* podwójny odst

ę

p */ 

 

  } 

  } 
void prn_info(char *pgm_name)  
  { 
   printf("\n%s%s%s\n\n%s%s\n\n", 
 

"Uzycie : ", pgm_name, " dane wyniki", 

 

"Zawartosc pliku dane bedzie skopiowana do ", 

 

 

"pliku wyniki z podwojnymi 

odstepami."); 
  } 
 

Przykład. 

 
#include 

<stdio.h> 

#include 

<stdlib.h> 

#include 

<ctype.h> 

 
main(int argc, char **argv) 
  { 
   int c; 
   FILE *fp, *tmp_fp; 
   FILE *gfopen(char *nazwa, char *tryb); 
 

if (argc != 2) 

 

  { 

 

      fprintf(stderr, "\n%s%s%s\n\n%s%s\n\n", 
 

 

"Uzycie: ", argv[0], " plik", 

 

 

"Zawartosc pliku bedzie podwojona", 

 

 

" o tekst z duzymi literami."); 

    

   exit(1); 

     }  
 

fp = gfopen(argv[1], "r+"); 

 

tmp_fp = tmpfile(); 

 

while ((c = getc(fp)) != EOF) 

background image

 115 

 

  putc(toupper(c), tmp_fp); 

 

rewind(tmp_fp); 

 

fprintf(fp, "------\n"); 

 

while ((c = gestc(tmp_fp)) != EOF) 

 

  putc(c, fp); 

  } 
 
FILE *gfopen(char *nazwa, char *tryb) 
  { 
   FILE *fp; 
 
 

if ((fp = fopen(nazwa, tryb)) == NULL) 

 

  { 

 

   fprintf(stderr, "Blad otwarcia pliku %s\n", 

 

 

 

 

nazwa); 

 

   exit(1); 

 

  } 

 

return(fp); 

  } 
 

FILE  *freopen(const  char  *filename,  const  char  *mode,  FILE 
*stream); 

#include <stdio.h> 
 
int main(void) 

   /* pzrekierowanie standoadowego struminenia 
wyj

ś

ciowego */ 

   if (freopen("OUTPUT.FIL", "w", stdout) 
       == NULL) 
      fprintf(stderr, "error redirecting 
stdout\n"); 
 
   /* pisanie do pliku */ 
   printf("This will go into a file."); 
 
   /* zamkni

ę

cie strumienia */ 

   fclose(stdout); 
 
   return 0; 

background image

 116 

size_t fread(void *ptr, size_t size, size_t n, FILE *stream); 
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream); 
 

#include <string.h> 
#include <stdio.h> 
 
int main(void) 

   FILE *stream; 
   char msg[] = "this is a test"; 
   char buf[20]; 
 
   if ((stream = fopen("DUMMY.FIL", "w+")) 
       == NULL) 
   { 
      fprintf(stderr, "Cannot open output 
file.\n"); 
      return 1; 
   } 
 
   /* write some data to the file */ 
   fwrite(msg, strlen(msg)+1, 1, stream); 
 
   /* seek to the beginning of the file */ 
   fseek(stream, SEEK_SET, 0); 
 
   /* read the data and display it */ 
   fread(buf, strlen(msg)+1, 1, stream); 
   printf("%s\n", buf); 
 
   fclose(stream); 
   return 0; 

background image

 117 

21.6.

 

BEZPOŚREDNI DOSTĘP DO PLIKU 

 
 

long int ftell(FILE *stream); 

 

 

- daje bieżące położenie wskaźnika pliku 

 

 

 

(liczba bajtów od początku pliku) 

 
 

fseek(FILE *wsk_pliku, n, miejsce) 

 

- przesuwa wskaznik pliku o n bajtów od miejsca 

 

(miejsce:  

SEEK_SET  

0 - początek, 

 

 

SEEK_CUR   1 - bieżąca pozycja, 

 

 

SEEK_END   2 - koniec pliku) 

 
void rewind(FILE *stream); 
fseek(stream, 0L, SEEK_SET) 
 
Przykład. 
Przepisanie pliku końca. 
 

#include  

<stdio.h> 

#define MAX 100 
 
main() 
  { 
   char nazwa_pliku[MAX]; 
   int c; 
   FILE *wp; 
 
 

fprintf(stderr, "\nWprowadz nazwe pliku: "; 

 

scanf("%s", nazwa_pliku); 

 

wp = fopen(nazwa_pliku, "rb"); 

 

fseek(wp, 0, 2); 

 

 

/* koniec pliku */ 

 

fseek(wp, -1,1); 

 

 

/* 1 do tyłu */ 

 

while (ftell(wp) >= 0) 

 

  { 

 

   c = getc(wp); 

 

 

/* 1 do przodu */ 

 

   putchar(c); 

 

   fseek(wp, -2, 1); 

 

/* 2 do tyłu */ 

 

  } 

  } 

background image

 118 

21.7.

 

UCHWYTY DO PLIKU  

Funkcje istnieją w DOS, Windows, OS2 i UNIX ale nie w ANSI 
#include <fcntl.h> 
#include<io.h> 
#include<sys\stat.h> 
int open(const char *path, int access [, unsigned mode]); 
 
Otwarcie pliku identyfikowanego przez path do czytania bądź pisania.  
 
O trybie otwarcia decyduje parametr access, do którego należy przypisać 
bitową kombinację następujących stałych zdefiniowanych w pliku fcntl.h. 
 
Flagi podstawowe: 
O_RDONLY 

Otwarcie tylko do czytania 

O_WRONLY 

Otwarcie tylko do pisania  

O_RDWR 

Otwarcie do czytania i pisania. 

 
Inne flagi: 
O_NDELAY 

Flaga ma znaczenie dla łączy komunikacyjnego i plików 
specjalnych. Nie ma żadnego znaczenia dla zwykłych 
plików czy katalogów. 

O_APPEND 

Znacznik pliku będzie ustawiany na końcu pliku przy 
każdej operacji pisania. 

O_CREAT 

Jeśli plik nie istnieje to zostanie stworzony a parametr 
mode posłuży do ustawienia praw dostępu do pliku. 
Jeśli plik istnieje to flaga ta nic ne wnosi. 

O_TRUNC 

Jeśli plik istnieje jego długość jest zmniejszana do 0. 
Atrybuty pozostają niezmienione. 

O_EXCL 

Dopuszczalne w połączeniu z O_CREAT. Istnienie pliku 
powoduje wystąpienie błędu. 

O_BINARY 

Otwarcie w trybie binarnym.(tylko DOS) 

O_TEXT 

Otwarcie w trybie tekstowym. 

 
Wartości parametru Mode (DOS,Windows 
)
 
S_IWRITE 

Prawo do pisania  

S_IREAD 

Prawo do  czytania 

S_IREAD|S_IWRITE 

Prawo do  czytania i pisania  

 
Funkcja zwraca nieujemną liczbę bedącą uchwytem do pliku 

background image

 119 

 lub -1 gdy wystąpił błąd. 
FILE *fdopen(int handle, char *type); 
handle
 - 

uczwyt do pliku  

type   - 

tryb otwarcia  ( rwar+  tb ) 

 

#include <sys\stat.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <io.h> 
 
int main(void) 

   int handle; 
   FILE *stream; 
 
   /* open a file */ 
   handle = open("DUMMY.FIL", O_CREAT, 
                  S_IREAD | S_IWRITE); 
   /* now turn the handle into a stream */ 
   stream = fdopen(handle, "w"); 
 
   if (stream == NULL) 
      printf("fdopen failed\n"); 
   else 
   { 
      fprintf(stream, "Hello world\n"); 
      fclose(stream); 
   } 
   return 0; 

int write(int handle, void *buf, unsigned len); 
 
handle - 

uczwyt do pliku  

buf  

wskaźnik do obszaru danych 

len  

ilość bajtów do zapisania 

 
int read(int handle, void *buf, unsigned len); 
 
int close(int handle); 

 

background image

 120 

 
#include <stdio.h> 
#include <io.h> 
#include <alloc.h> 
#include <fcntl.h> 
#include <process.h> 
#include <sys\stat.h> 
#define BUFSIZE 1024 
int main(void) 

 

void *buf; 

 

int hin,hout, bytes; 

 

long total=0; 

 

buf = malloc(BUFSIZE); 

 

if ((hin = 

 

open("handle.bak", O_RDONLY, S_IWRITE | 

S_IREAD)) == -1) 
 

 

printf("Error Opening Input File\n"); exit(1); 

 

  if ((hout = open("TEST.$$$", O_WRONLY | O_CREAT | 
O_TRUNC,0400)) == -1) 
 

 

printf("Error Opening Output File.\n");exit(2); 

 

 

do { 

 

if ((bytes = read(hin, buf, BUFSIZE)) != -1) 

   { 
  printf("Read: %6d  bytes read.\r", total+=bytes); 
 

 if ((bytes = write(hout, buf, bytes)) == -1) 

 

 { 

 

printf("Write Failed.\n"); exit(4); 

 

 } 

   } 
 

 }while((bytes>0)); 

    

  if(bytes==-1) 

 

   { 

 

    printf("Read Failed.\n"); exit(3); 

 

   } 

close(hin); close(hout); 
return 0; 

background image

 121 

background image

 122 

22.

 

C w systemie UNIX 

 

22.1.

 

Kompilator języka ANSI C 

W kazdej instalacji UNIX powinien istnieć standardowy kompilator jezyka 
C o nazwie cc 
Jego wywołanie: 

cc nazwa  

 

cc nazwa –o wynik 

gdzie  

nazwa  nazwa pliku z kodem zródłowym – niektóre kompilatory 

wymagają aby plik posiadał rozszerzenie .c 

wynik  nazwa pliku wynikowego (domyślna  w pierwszej wersji 

wywołania to a.uot 

 

Uwaga 
System UNIX rozróżnia wielkość liter w poleceniach nazwach plików i 
katakogów. 

22.2.

 

Pliki w systenie UNIX 

/  

katalog główny   

root 

/usr/myid 

katoalog użytkownika  

HOME    ~ 

 
Pojęcie pliku i katalogu w systemach DOS i UNIX są podobne. Bardzo 
ważna różnicą są pwawa dostępu do pliku (katalogu) : prawo czytania 
(read) pisania (write) i uruchamiania (execute).  

 
Uprawnienia dostępu ósemkowo 
 
 
 

Użyteczne stają się tu funkcje dostępu do pliku poprzez uchwyty: 
 
int open(const char *path, int access [, unsigned mode]); 
int write(int handle, void *buf, unsigned len); 
int read(int handle, void *buf, unsigned len); 
int close(int handle); 

 

u  0400  0200  0100 

040  020  010 

04 

02 

01 

background image

 123 

   

22.3.

 

Funkcje inicjacja procesu 

int execl (char * f_path, char* arg1,...,null); 
int execve(char *f_path, char *argv[],char *env[]); 
 
Funkcja 

Format parametrów  Przekazywanie środowiska  PATH 

Execl 

Lista 

Automatyczne 

Nie 

Execv 

Tablica 

Automatyczne 

Nie 

Execle 

Lista 

Ręczne 

Nie 

Execve 

Tablica 

Ręczne 

Nie 

Execlp 

Lista 

Automatyczne 

Tak 

Execvp 

Tablica 

Automatyczne 

Tak 

 
  
Funkcja systemowa exec dokonuje ponownego zainicjowania procesu na 
podstawie wskazanego programu. Proces pozostaje zatem ten sam, a 
zmianie ulga wykonywany program. 
 
Proces - to środowisko wykonywania programu, któte składa się z 
segmentu    instrukcji,  segmentu  danych  u
żytkownika  oraz 
segmentu danych systemowych 
 
Program  -  to  plik  zawierający  instrukcje  i  dane  służące  do 
inicjalizacji procesu. 
 

excectest() 

printf("To jest przyklad"); 
    
execl("/bin/echo","echo","wywaloania","funkcji","ex
ecl",   
               NULL); 
 

 perror("EXEC:"); 


 

Funkcja zwraca -1 w przypadku błędu. W przypadku wywołania 
poprawnego powrót nie nastąpi. 

background image

 124 

22.4.

 

 int fork() 

 
Funkcja fork tworzy nowy proces, ale nie inicjuje go na podstawie 
jakiegoś nowego programu. Segmenty instrukcji, danych użytkownika i 
systemowych są wielokrotnymi kopiami starego procesu. 
Funkcja ta tworzy proces potomka. Zazwyczaj proces potomny wywołuje 
funkcją exec, a proces macierzysty albo czeka na zakończenie procesu 
potomnego, albo zajmuje się czymś innym. 
Funkcja fork gdy wystąpi błąd (wyczerpanie zasobów systemu) -1. W 
innych przypadkach do procesu potomnego zwraca 0, przodek otrzymuje 
identyfikator procesu potomka. 
Identyfikatory potomka i przodka są różne- przecież to są inne procesy 
Potomek otrzymuje kopie deskryptorów do plików od przodka, jednak 
wskaźniki do pliku są wspólne- lseek zmieni ich położenie dla obu 
procesów, a zamknięcie zamknie dostęp tylko dla jednego z nich 
Czas wykonywania potomka będzie na starcie wynosił 0 
 

testfork() 

 ind idp; 
 printf(„Poczatek”); 
 idp=fork(); 
 printf(„powrot %d”,idp); 

 
wynik: 
 

Poczatek 

 

powrot 0 

 

powrot 13412 

 

22.5.

 

Inne funkcje systemowe 

void exit(int stan) 

zakończenie procesu 

int wait(int *w)  czekaj na zakończenie potomka 
int chdir(char* sciezka) 

zmień bieżący katalog 

int pipe(int pdeskr[2]) tworzy łącze komunikacyjne /0  OK  -1 błąd / 
pdeskr[0] 

do czytania 

pdeskr[1] 

do pisania 

int write(int handle, void *buf, unsigned len);  

pisz do łącza (pliku) 

int read(int handle, void *buf, unsigned len); 

czytaj z łacza (pliku) 

background image

 125 

 
main() 

int fork(void), value; 
 
value = fork(); 
value = fork(); . 
printf("In main: value = %d\n", value); 

 

In main: value = 1788 In main: value = 0 In main: value = 0 In main: value 
= 1789 
 
Kolejność wykonywania procesów - niedeterministyczna. 
 
Komunikacja między procesami: 
int pipe(int pd[2]); 
Tworzenie bufora we/wy pomiędzy procesami równoległymi.  
pd[0], pd[1]- opisy plików (input, output) 
Wartością pipe jest 0 gdy połączenie jest utworzone lub -1 gdy nie. 

 
/* równoległe sumowanie wierszy macierzy */ 
#include       <stdio.h> 
#include       <stdlib.h> 
 
#define N 3 
 
int       add_vector(); 
void error_exit(); 
int fork(); 
int pipe(); 
int read(); 
int write(); 
 
main() 
  { 
   int a[N][N],  
       i, row_sum, sum = 0, 
       pd[2]; 
a[0][1] = a[0][2] = a[0][0] = 1; 

background image

 126 

a[2][1] =   a[2][2] =    a[2][0] = 2; 
a[1][1] = a[1][2] = a[1][0] = 3; 
     if (pipe(pd) == -1) 
          error_exit("pipe failed"); 
     for (i = 0; i < N; ++i) 
       if (fork() == 0) 
          { 
           row_sum= add_vector(a[i]); 
         if(write(pd[1],&row_sum, sizeof(int))==-1) 
               error_exit("write failed"); 
           printf(" ******* %d \n", row_sum); 
           return; 
          } 
     for (i = 0; i < N; ++i) 
      { 
       if (read(pd[0], &row_sum, sizeof(int)) ==-1) 
          error_exit("read failed"); 
       sum += row_sum; 
      } 
     printf("Suma el tablicy = %d", sum); 
  } 
 
int add_vector(v) 
int v[]; 
  { 
   int i, vector_sum = 0; 
 
     for (i = 0; i < N; ++i) 
  {  printf("v[%d]= %d\n", i,v[1]); 
     vector_sum += v[i]; 

     return vector_sum; 
  } 
void error_exit(s) char *s; 
  { 
     fprintf(stderr, "\n ERROR: %s \n", s); 
     exit(1); 
  }