plikus pl DOBRE(przeczytac) id Nieznany

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:
c

d

f

s

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żenie) instrukcja

{ 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

8

0 do 255

char, signed char

8

-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

0

0

1

1

2

2

. . .

9

9

A

10

B

11

C

12

D

13

E

14

F

15


h

n

h

n-1

. . .h

1

h

0


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

1

-i - 5*j >= k+1

0

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

3 < j < 5

1

j = 4

1

j = 7

1


Przykład.

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

!c

0

!(i - j)

1

!i - j

-7

!!(x+y)

1

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 a|b

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

0

0

0

0

0

1

0

0

1

1

0

1

0

1

1

1

1

1

0

1


long

a = 33333, b = -77777;


a

00000000 00000000 10000010 00110101

b

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';

c

01011010

background image

26

c << 1

10110100

cc>>1

01011010


x << y mnożenie x*2

y

x >> y dzielenie x / 2

y


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)

5


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)

1

sum(2)

2 + sum(1)

3

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

b

w


1

2

---------->



p = &a;

a

b

w


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)

1


* * & p

* (* (& p))

3


r = & x

r = (& x)

ą

d


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

s

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

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

L


*(p->nazwisko + 2)

(p->nazwisko)[2]

w


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;

1

NULL

2

NULL

3

NULL


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

1

---->

2

---->

3

NULL

a.nast->dane

2

a.nast->nast->dane

3


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

---->

a

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;

}

x

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

s

x

x

s

x

s

x

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;
}

s

x

3

2

1

4

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

0

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)

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 O 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

b

f

g



c

d

e

h liście



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


Plik drzewo.h:

#define

NULL

0


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

}

}


G



D

I



B

F

H

J



A

C

E


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]

a



b

f

g



c

d

e

h

NULL


NULL

NULL

NULL NULL



Plik w_drzewo.h:

#define

NULL

0

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

r

w

x

u 0400 0200 0100
g

040 020 010

a

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);
}


Wyszukiwarka

Podobne podstrony:
5 2 3a CCNA1 Laboratorium pl id Nieznany (2)
pieniadz PL 2012 slajdy 1 10 id Nieznany
5 1 10 CCNA1 Laboratorium pl id Nieznany (2)
5 3 pl warunki techniczne id 39 Nieznany (2)
dobre bezpieczenstwo id 138306 Nieznany
Dobre wypracowania id 138349 Nieznany
notatek pl 111 555 id 321325 Nieznany
Chisel 75WG SDS 20090908 PL id Nieznany
Fundamenty z murator dom pl id Nieznany
ICN Opis pracy PTP 2009 pl id 2 Nieznany
Cennik INTERIA PL 2013 03 21 id Nieznany
5 2 3a CCNA1 Laboratorium pl id Nieznany (2)
pieniadz PL 2012 slajdy 1 10 id Nieznany
cw 16 odpowiedzi do pytan id 1 Nieznany
Opracowanie FINAL miniaturka id Nieznany
How to read the equine ECG id 2 Nieznany
PNADD523 USAID SARi Report id 3 Nieznany

więcej podobnych podstron