VIŠA TEHNIČKA ŠKOLA SUBOTICA
PREDAVANJA – PROGRAMSKI JEZIK C
Rukovodilac kursa/nastavnik: Mr. Ištvan Boroš email:
[email protected]
Uvod Kratka istorija C jezika ! ! ! ! !
1969: operativni sistem UNIX, Bell Laboratories 1970-74: Dennis Ritchie projektuje jezik C, UNIX „prepisuju“ na C (←BCPL 1963, B jezik 1970) 1978, 1988: Kernighan&Ritchie: The C Programming Language Od onda je C popularan, prenosiv jezik. Standard ANSI C poznaje svaki moderan C prevodilac (1989), zato C nije vezan ni za jednu mašinu niti operativni sistem. Krajem 1980.-tih raste popularnost takozvanih objektorijentisanaih jezika, te se pojavljuju i OO proširenja jezika C: C++, Objective C.
Prednosti C jezika • • • •
Standardizovan, zato su C programi prenosivi. Usled popularnosti postoji C prevodilac za svaku mašinu i operativni sistem (ima još mnogo dobrih jezika, koji ne poseduju to svojstvo). Podržava modularno programiranje, i na taj način i pisanje velikih programa u okviru timskog rada. Sretna kombinacija discipliniranog, čistog programskog stila i fleksibilnosti (ali upravo zbog fleksibilnosti je moguće napisati ogromne, nepregledne, neispravljive programe sa puno grešaka).
Osnovna struktura C programa /* C osnovni program */ #include
int main(void) { printf(„Zdravo!\n"); return 0; }
• • • • •
Razmaci i novi redovi se mogu proizvoljno koristiti (na taj način činimo programski tekst preglednijim). Ključne reči su obavezno pisane malim slovima! Svaka naredba se završava sa ; . Komentari su ograničeni sa /* i */ i mogu se, prostirati i preko više redova. Struktura glavnog programa:
int main(void) { deklaracije promenljivih; naredbe; return 0; }
Osnovni tipovi promenljivih (kratak pregled) • celobrojna: int i,j,k; i=1; j=-5; • leteći zarez: float f,g; f=1.234;g=-4e4; • karakter: char c,d; c='A'; d='\n';
• pointer (pokazivač): int *p; p=&i; • string: char s[21]; char *t; t=„Tekst";
Identifikatori, deklaracije promenljivih •
• •
Pre upotrebe svaku promenljivu obavezno deklarisati! Oblik deklaracije: tip ime; ili tip ime1, ime2, ime3; Primer: int a,b,c,d; Ime promenljive može sadržati slova, cifre i „_” ali ne može da počne sa cifrom. Primer: char _dugacki_naziv2promenljive; Mala i velika slova se razlikuju! Primer:
float top, TOP, Top, tOp; /* 4 različite promenljive */
Operatori i izrazi Aritmetički operatori i izrazi • • •
Retka osobina medju programskim jezicima višeg nivoa! Bitove, koji se “prelivaju” izgubimo, a “ulazeći” bitovi su 0. To je množenje sa stepenima broja 2, odnosno deljenje sa zaokruživanjem.
Osnovne operacije: + - * / Negacija: Deljnje sa ostatkom: % Primer: 11%4 → 3 Pomeranje bit po bit: >> << • Primer: 7<<2 → 28 35>>1 → 17 Primer složenog aritmetčkog izraza: (-a+b)*(a%(c-12))
Logički operatori i izrazi Vrednost logičkih izraza je 1 ili 0 (istina ili neistina), koriste se u uslovima grananja i ciklusa. • logička negacija: ! • ispititati jednakost: == !=
Pr: !1→0 !0→1 Pr: 26==26→1(istina) 27!=27→0(neistina) Pr: 5>8 → 0 (neistina) • relacije: < > <= >= 2<=2 → 1 (istina) • logički i odnosno ili: && || Pr: 1&&0 → 0 (neistina) 1||0 → 1 (istina) • kratki spoj: x||y++ za x<>0 "y se ne inkrementira! • primer složenog logičkog izraza: (a>b)||((a==b)&&(c>d))
Operatori dodeljivanja vrednosti U jeziku C i dodeljivanje vrednosti je operator, pa je moguje primeniti unutar izraza! Dodeljivanje vrednosti vraća novu vrednost promenljive (to je opšta osobina i kod izraza). Primer: izraz a=(b+5) prima vrednost b+5, ali i promenljiva a će primiti istu vrednost. • Dodeljivanje vrednosti: = Pr: a=5; b=077; c=0xff;
/* 10,8,16 sist. */
• Složeniji primer:a=((b=2)+3); /* b=2, a=5 */ a=b=c=8; /* višestruko dodeljivanje vredn. */ ČESTA GREŠKA! a=b i a==b je različito!
• Modifikacija vrednosti: += -= *= /= %= >>= <<= Pr. a+=4 znači isto kao i a=a+4 c*=2 je isto, kao i c=c*2 , ali je kod brži. • Inkrementacija, dekrementacija: ++ -Pr. a++ znači vrednost a poraste za 1. Varijante: a++ odnosno ++a. Razlika: a svakako poraste za 1ali u izrazu a++ a znači ulaznu a u izrazu ++a znači izlaznu vrednost! Operator „--” na sličan način smanjuje vrednost promenljive za 1. Primer: nakon int a=5; imamo: b=a++ → a postane 6, a b prima vrednost 5 (postfiks) b=++a → obe promenljive će imati vrednost 6 (prefiks) C jezik je vrlo sažet, sa jednom naredbom možemo istovremeno izvršiti više operacija!
Ostali operatori • * & (videti i kod pokazivača) • Bit po bit konjunkcija, disjunkcija, ekskluzivna disjunkcija i negacija: & | ^ ~ 6=110 Pr. 6&3 → 2 3=011 6|3 → 7 & 010 6^3 → 5 | 111 ~6 → -7
" 2 " 7 ^ 101 " 5 ~ 11111001 " -7
• Uslovni operator: f ? b : c → ako je f istina (<>0), daje vrednost od b, inače daje vrednost od c Pr. a
Precedencija odredjuje redosled (prioritet) izvršavanja operacija kod složenih izraza, (upotrebom zagrada možemo promeniti). Precedencija operatora u opadajućem redosledu: 1. !,++,--,- (negacija i komplemenat 2), ~ (negacija bit po bit i komplemenat 1, NOT), * (pointer), & (adresa) 2. *,/,% (množenje, deljenje, ostatatak) 3. +,4. <<,>> (pomeranje bitova) 5. <,<=,>,>=
6. ==,!= 7. & (AND po bitovima) 8. ^ (XOR po bitovima) 9. | (OR po bitovima) 10. &&
11. || 12. "?:" 13. =,+=,-= itd. 14. , (operator zapeta)
• Operatori u istom redu imaju istu precedenciju. • Operatori istog prioriteta izvršavaju se s leva na desno: primer a-b-c znači isto kao i (a-b)-c. IZUZETAK: klase 1., 12., 13. kod kojih se grupisanje vrši s desna na levo. Primer: a=b=0 znači isto kao i a=(b=0). • Nije grečka, ako radi jednoznačnosti stavljmo zagrade, koje su inače „suvišne”. Primer: (a>b) || ((a==b) && (c>d)).
KEYWORD TABLE auto
double
int
struct
break
else
long
switch
case
enum
register
typedef
char
extern
return
union
const
float
short
unsigned
continue
for
signed
void
default
goto
sizeof
volatile
do
if
static
while
Osnovni tipovi Jezik C ima mali ali jasan skup osnovnih tipova koje je potrebno deklarisati. Tom prilikom može da se zadaje i početna vrednost. Pr. int i=1,j=2;
Celobrojni tipovi 1. signed (sa predznakom: -32768..32767, podrazumeva se) 2. unsigned (0..65535), odnosno short, int, long Pr. unsigned long l; sizeof(short)<=sizeof(int)<=sizeof(long) sizeof je operator: vraća dužinu tipa u bajtovima. pr. Turbo C++ : sizeof(int)!2, sizeof(long)!4 NE KONTOLIŠE SE PRELIVANJE! Naprimer izraz int i=32767+1; menja i na -32768 (pod uslovom, da je sizeof(int)=2).
Racionalni tipovi 1. float 2. double 3. long
double
sizeof(float)<=sizeof(double)<=sizeof(long double)
Automatske konverzije tipova Automatske konverzije primenjuje C prevodilac kod mešovitih izraza. Pr. 1+5.6 → 1.0+5.6 → 6.6 Suština: uvek prelazi "netačniji" tip u "precizniji". Eksplicitna konverzija tipa: int a; a=sqrt((double)25); zato što kvadratni koren očekuje broj double tipa
Logički tip C koristi CELOBROJNI TIP za čuvanje logičkih vrednosti. 0 – je netačno, sve ostalo - tačno. Pr. if(1) x=5; → x=5 će se svakako izvršiti! b=(a!=a+1); → b prima vrednost 1.
Karakter tip Automatski se konvertuje u celobrojni tip. Pr. 'A' znači isto kao 65 (ASCII kod). char c='0'; int i=c+5; → je korekno dodeljivanje. Varijanta sa predznakom i bez znaka: → -128..127 signed char unsigned char → 0..255 Osnovno značenje je različito kod različitih prevodilaca!
Tip nabrajanja Definicija tipa: enum naziv Iza } je neophodan tačkazarez!
{element1,element2,...};
enum naziv je novi tip. Sada mogu deklarisati: enum naziv v1,v2,v3; Od sada
Navedeni simboli mogu se tretirati kao celi brojevi: element1=0, element2=1 itd. Primer: enum raddan {ponedeljak, utorak, sreda, četvrtak, petak}; enum raddan a,b; a=ponedeljak; b=a+2; → b će biti četvrtak (ili 3).
Nizovi Pr. int a[20]; Biće niz sa 20 elemenata: a[0],a[1],..,a[19] U deklaraciji dimenzija niza ne može biti promenljiva! Inicijalizacija jednodimenzionih nizova: float f[]={1.0,3.14,2.71}; U gornjem slučaju dimenziju niza C sam prepoznaje. Višedimenzioni nizovi: char c[10][20]; c[5][4]='k'; Skladištenje elemenata je vrsta po vrsta. C ne kontroliše kraj!
Strukture Struktura odgovara pojmu rekorda kod nekih drugih jezika. Definisanje nove strukture: struct naziv { tip1 naziv1; tip2 naziv2; ... }; Primer:
struct lice{ char ime[25]; int starost,visina;}; Deklarisanje promenljive tog tipa:
struct lice ja; ja.visina=187;
Unija Može da sadrži više tipova na istom mestu. Liči na strukturu, ali ovde elementi zauzimaju ista memorijska mesta! Primer: union un { int i; char s[2]; }; union un a; a.i=0x7978; /* vrednost od a.s biće ”xy” */ Unija je zamena za varijante rekorde kod drugih jezika.
Definisanje sopstvenog tipa typedef int masa; → masa če biti celobrojni tip masa s1,s2; → s1,s2 biće celobrojne promenljive typedef struct { char ime[25]; int starost,visina; } lice;
(Novi naziv je na kraju!)
lice ja;
(Nepotrebna reč struct!)
typedef enum {musko, zensko} pol; pol ivica=musko; pol marica=zensko; Definisanje niza: typedef char pttbroj[5];
Pokazivači (pointeri) Pokazivači sadrže memorijske adrese! Svakom tipu pripada jedan tip pokazivača, koji čuva adresu promenljive datog tipa. Pr: nakon int
*p; p “pokazuje na ceo broj".
"int *“ je tip promenljive p. Celobrojna vrednost što pokazuje p je: *p
& je adresni operator: &a je adresa promenljive a
Primer:
{ char a,b; char *p; a='a'; p=&a; b=*p+1; } Nakon izvršenja vrednost od b biće 'b'. Primer: prepisivanje promenljive (pointer+eksplicitna konverzija tipa):
int x=10; *(int*)&x=20; Oprezno sa pointerima! Vrlo lako može da padne program!
Veza nizova i pokazivača int a[20];
↓ Zauzima 20*sizeof(int) bajtova u memoriji i a kao pointer pokazuje na početak. Zato pr. *a i a[0] znače isto:
*a a[0]
....
↑ a
*(a+1) *(a+2) a[1] a[2]
↑ a+1
↑ a+2
....
↑ a+3
Aritmetika pointera Pointeru se može dodaticeo broj k, rezultat je pointer, koji pokazuje na poziciju pomerenu za k bajtova (videti prethodnu sliku). Primer:
int a[5]={10,20,30,40,50}; int *p=a; p+=4; --p; Rezultat: p pokazuje na a[3] (*p=40). Pažnja! Takodje se a može primeniti kao pointer bez modifikacije! pr. ++a javlja grešku!
Stringovi String je znakovni niz koji je zaključen sa bajtom vrednosti 0 na kraju ('\0').
('\0' != '0'). Pr.
char s[]="Neki tekst"; je niz sa 11 elemenata, s[0]='N', s[10]='\0'
Pažnja! Definisani s sa s="Novi tekst"; je pogrešan, jer hoće da modifikuje s- kao pokazivača!
#include #include int main(void) /* ”Hello Hello” */ { char s[10]; char *str="Hello"; strcpy(s,str); printf("%s %s\n",s,str); return 0; }
Ulaz i izlaz podataka Jezik C u osnovnoj definiciji ne sadrži operacije ulaza i izlaza. Te funkcije se nalaze u takozvanim standardnim bibliotekama. Zahtev za upotrebu standardne biblioteke saopštavamo C prevodiocu: funkcije koje su deklarisane u odredjenim fajlovima učitavamo direktivom #include. Na primer za prost ulaz i izlaz potrebno je: #include
Ulaz i izlaz u jednostavnom slučaju obavlja se sa takozvanog standardnog inputa odnosno na standardni output pod kojima se podrazumeva tastatura odnosno ekran. Medjutim to se može i preusmeriti. Formatirani izlaz se realizuje sa printf funkcijom. Primer: int i=6; printf("Rezultat: %d\n",i); \a - zvuk, \b - backspace, \f - strana, \n - novi red, \r - na početak reda, \t - horiz. tab, \v - vert. tab, \’ - apostrof, \” - navodnik, \\ - backslash, \? - upitnik, \ooo - ASCII okt. karakter, \xhh - ASCII hex karakter
Osobine: • To je funkcija sa proizvoljnim brojem argumenata, prvi parametar je string, koji je sastavljen od • normalnih karaktera i • specifikatora formata koji počinju sa %, a ostali parametri su podaci koji se ispisuju, svakome treba da odgovara po jedna specifikacija formata. • Broj i tip specifikacija treba da odgovara parametrima koji se ispisuju, ali C se neće pobuniti ni kod pogrešnik specifikatora!
• Mogući formati su: %d - ceo broj sa znakom %i - ceo broj sa znakom %o - oktalni ceo broj bez znaka %x,X - heksadecimalni ceo broj %u - ceo broj bez znaka %c - karakter %s - string (char *) %f - double %e,E - double, u eksponencijalnom obliku %g,G - double, u obliku koji zavisi od veličine %p - pointer %% - sam "%" karakter
Izmedju % i slova može da stoji još: • znak minus za levo poravnanje • znak plus za ispisivanje predznaka • broj koji odredjuje minimalnu širinu polja • tačnost (broj decimala, rastavljen od prethodnog broja tačkom, podrazumevana vrednost je 6) • slovo h ili l, za ispisivanje short/ long brojeva Primer za ispisivanje brojeva 4 i 3.14159265358979: %d →4 %f →3.141593 %5d → 4 %e →3.141593e00 %+5d → +4 %10f → 3.141593 %8.3f → +3.142
Formatirani ulaz se obavlja pomoću scanf funkcije: int i; scanf("%d",&i); Funcija scanf čita iz stdin toka, koji ponekad može biti popunjen nepotrebnim podacima. Isprazniti sa: fflush(stdin); • Ova je funkcija sa proizvoljnim brojem argumenata, prviparametar je uvek string, • ignoriše razmak, novi red i tab • normalni karakteri će se pojaviti kod "traženja" ulaza • koristimo specifikacije koje počinju sa % • Ostali parametri su adrese promenljivih (pokazivači), na koja mesta treba smestiti učitane
vrednosti. Svakom podatku treba da odgovara jedna specifikacija. • I ovde bi bilo dobro da se specifikacije i ostali parametri slažu po tipu, ali C prihvata i pogrešnu specifikaciju! • Moguće specifikacije su: %d - dekadni ceo broj sa znakom %o - oktalni ceo broj bez znaka %x,X - heksadecimalni ceo broj %u - ceo broj bez znaka %c - karakter %s - string (char *)
%f,e,g - broj sa pokretnim zarezom %p - pointer [...] [^...] - skup pripadanja/suprotno od (char*) Izmedju % i navedenih slova može da bude još i slovo h ili l ako čitamo short ili long broj. Funkcija scanf vraća broj uspešno učitanih podataka. To možemo da upotrebimo i u traženju (sprečavanju) greške. sscanf ! čita iz stringa u promenljive.
Input-output po karakterima int getchar(void); Čita po jedan karakter sa stdin. U slučaju greške ili na kraju inputa vraća karakter EOF. ( EOF je simbol definisan unapred i koristi se za obeležavanje kraja fajla ili greške. U Turbo C++ ima vrednost –1.) int putchar(char c); Ispisuje karakter c na stdout. Vraća vrednost karaktera c a u slučaju greške EOF.
Primer: Učitani tekst ispisujemo VELIKIM slovima.
#include void main(void) { int c; printf("Molim jedan tekst, Ctrl+Z=kraj : "); while((c=getchar()),c!=EOF) { if(c>='a' && c<='z') operator zapeta, while dobić c+='A'-'a'; vrednost 2. člana putchar(c); } }
Ulaz i izlaz stringa char *gets(char *s); Učitaće niz karaktera koji je završen sa Enter. (Enter ne pripada učitanom stringu!). Vraća vrednost s, a u slučaju greške ili na kraju inputa NULL. (I NULL je unapred definisan simbol koji se upotrebljava za signalizaciju greške.) int puts(char *s); Ispisuje na standardni outputr string s i jedan karakter za novi red. U slučaju greške vraća karakter EOF, inače vraća nenegativan broj.
Naredbe •
Izraz kao naredba: Pr. c+6; je ispravna naredba, a nema uticaj. Dodeljivanje vrednosti je takodje izraz, a svaki izraz je naredba, pr. a=c+6; • Prazna naredba: ; (često se primenjuje u unutračnjosti ciklusa) • Složena naredba (blok): Oblik: Par zagrada {...} je sličan paru BEGIN..END Pascal-a, { ispred } poslednja naredba deklaracije; se takodje završava sa ; a naredbe; iza } nema ; ! }
•
Uslovna naredba Oblici:
if(uslov) naredba; if(uslov) naredba1; else naredba2; Primedbe: uslov je obavezno u zagradi! naredba u većini slučajeva je složena naredba, tada se iza naredbe ne stavlja ; Primer: Poredjati a i b u rastući redosled: if(a>b) { U Pascal-u ZABRANJENO! int t; t=b; b=a; a=t; } U bloku definišemo privremenu promenljivu!
U sva tri slučaja izlaz ako je uslov NEISTINIT!
• Ciklusi While-ciklus Oblik: while(uslov) naredba; Testiranje unapred. Primer: Izračunati N!
{ int f,i; f=1; i=2; while(i<=N) f*=i++; } Do-ciklus Oblik: do naredba; while(uslov); Naknadno testiranje, jezgro ciklusa se izvrši bar jednom.
For-ciklus Oblik:for(pocetnavrednost;uslov;korak) naredba; Isto kao i:
Beskonačni ciklusi: pocetnavrednost; while(uslov) a) for(;;) naredba; { naredba; b) while(1) naredba; korak; c) do naredba; while(1); } Primer: ciklus od m do n: for(i=m; i<=n; ++i) naredba; Primer: N! sa for ciklusom { int f,i; f=1; for(i=2; i<=n; ++i) f*=i; }
• Višestruko grananje programa Oblik:
switch(izraz) { case slucaj1: naredbe1; case slucaj2: naredbe2; (...) default: naredbe; } izraz mora biti celobrojnog tipa. Nakon izvršenog odgovarajućeg slučaja program nastavlja sa sledećim slučajem. Rešenje: naredba break.
Primer: primer za rukovamje tasterima
switch(taster) { case ‘l': /* levo */ break; case ‘d': /* desno */ break; case ‘p': /* PALI!!! */ break; }
• Ostale naredbe (NAREDBE SKOKOVA) Break
Oblik:
break;
Iz dubine ciklusa i switch-grananja skače na prvu naredbu iza ciklusa, odnosno grananja. Continue
Oblik:
continue;
Iz dubine jezgra ciklusa skače na kraj i nastavlja sa testiranjem uslova ciklusa. Goto
Oblik:
goto labela;
Program nastavlja rad sa naredbama iza reda označenog sa "labela:". Nemoguće je „uskočiti” sa goto u blok (naprimer u jezgro ciklusa)! Upotrebljava se naprimer za napuštanje koncentričnih ciklusa. Svaki program se može napisati i bez goto. Upotreba te naredbe se kosi sa filozofijom struktuiranog programiranja, ali u jeziku assembly podrazumeva se normalna upotreba .
Fajlovi - rukovanje fajlovima Pre pristupanja jednom fajlu na disku prvo ga treba otvoriti. Pošto sa više fajlova možemo istovremeno raditi, potrebna je njihova identifikacija. To je unapred definisani tip FILE. Na fajlove ćemo se pozivati pomoću FILE * pointera. Otvaranje fajlova FILE *fopen(char *ime,char *mod); • ime je ime fajla za otvaranje (po potrebi zajedno sa putanjem),
• mod je metod otvaranja, što može biti sledeće: "r" - otvoriti fajl samo za čitanje, "w" - samo za pisanje (ako ne postoji, kreira ga) "a" - dopisati "r+" - pisanje i čitanje (samo ako već postoji) "w+" - pisanje i čitanje "a+" - čitati i dopisati U stringu svakog metoda može da stoji još jedno "b" ili "t" u zavisnosti od toga, da li je fajl binarni ili tekst fajl. Otvaranje vraća kod uspešnog otvaranja indentifikator FILE * pointer, a u slučaju greške NULL.
Zatvaranje fajla int fclose(FILE *f); u slučaju greške vraća EOF. Dosad upoznate funkcije ulaza i izlaza u slučaju fajla su: int fprintf(FILE *f, char *s, ...); int fscanf(FILE *f, char *s, ...); int getc(FILE *f); int putc(int c, FILE *f); char *fgets(char *s, int n, FILE *f); int fputs(char *s, FILE *f);
Specijalni fajlovi ( imaju tip FILE * , ali nisu fajlovi na disku, unapred su definisani i nije potrebno njihovo otvaranje): stdin stdout stderr stdaux stdprn
- standard input (tastatura) - standard output (ekran) - standardni kanal greške (ekran) - standard auxiliary device (COM1) - standard printer (LPT1)
Primer: printf("Zdravo!\n"); fprintf(stdout,"Zdravo!\n"); /*identično!*/ Preusmeravanje ulaza i izlaza u DOS-u: progfajl, progfajl2, prog1|prog2
Binarni fajlovi (čitanje, lokalizacija pozicije):
• • • • • •
pisanje
pozicioniranje
i
int fread(void *p, int size, int n, FILE *f); čita proizvoljni podatak iz fajla p je adresa na koju želimo učitati podatak void * je specijalni pointer tip: prihbvata bilo koji pointer. Primer: popuna niza iz fajla: size dužina podatka int t[10]; n broj podataka FILE *f=fopen("ul.dat","rb") f je fajl iz kojeg čitamo fread(t,sizeof(int),10,f);
int fwrite(void *p, int size, int n, FILE *f); • • • • •
proizvoljni podatak ispiše u fajl p je adresa sa kojeg ispisujemo podatak size je dučina podatka n je broj podataka f je fajl u kojeg pišemo.
int fseek(FILE *f, long off, int odakle); • u f fajlu postavlja aktuelnu poziju na bajt broj off • odakle definiše u odnosu na koju poziciju, što može biti jedna od sledećih: SEEK_SET - od početka fajla SEEK_CUR - od dosadašnje pozicije SEEK_END - od kraja fajla. • u slučaju uspeha vraća 0, nije 0 u slučaju neuspeha. Na kraju long ftell(FILE *f); vraća aktuelnu poziciju, u slučaju neuspeha vraća -1.