Zadatak 1 strukture (C110) P2: Jedanaesta nedelja Strukture i liste Date su sledeće deklaracije: typedef int CeoBroj; typedef int *PokazivacNaCeoBroj; typedef int NizCelihBrojeva[100]; CeoBroj *pokaza; PokazivacNaCeoBroj pokazb; NizCelihBrojeva niz; struct { int x,y; zapisa; struct { int x,y; zapisb, zapisc; Koje od sledećih naredbi dodele su semantički ispravne? A) pokaza = pokazb; B) pokaza = niz; C) niz = pokaza; D) zapisb = zapisc; E) zapisa = zapisb; 1/38 2/38 Strukture na jeziku C (1) Strukture na jeziku C (2) Strukture su složeni tipovi podataka koji se sastoje od određenog broja (raznorodnih) elemenata elementi struktura nazivaju se poljima struktura i svako polje se obeležava posebnim identifikatorom za razliku od niza, polja strukture ne moraju biti istog tipa polja strukture mogu biti proizvoljnog prostog ili složenog tipa za pristup svakom polju se koristi njegovo ime, koje je vidljivo samo unutar strukture u kojoj je definisano (strukturni doseg) Definisanje strukture: struct [ime] {niz deklaracija za polja [promenljive]; ime strukture je opciono, a zajedno sa ključnom rečju struct se koristi za definisanje promenljivih koje se tipa strukture niz deklaracija za polja strukture predstavalja niz naredbi za definisanje promenljivih opciono se nakon navođenja opisa strukture odmah mogu deklarisati i odgovarajuće promenljive tipa te strukture Promenljiva tipa strukture zajedno sa svojim poljima čini jedinstven objekat u memoriji i može biti povratna vrednost funkcije Primer: struct tacka { int x,y; a,b,c; struct tacka levo_gore, desno_dole; struct krug { float r; int x, y; ; Pristup poljima strukture: preko promenljive strukturnog tipa korišćenjem operatora. a.x = 5; preko pokazivača na promenljivu tipa strukture korišćenjem operatora -> pa->x = 5; 3/38 4/38
Definisanje korisničkih tipova Zadatak 1 - rešenje Programski jezik C dozvoljava definisanje korisničkih tipova podataka Novi tip se definiše na taj način što se prvo navede ključna reč typedef, a zatim ime i opis novog tipa typedef int CeoBroj; typedef struct {int x,y; Tacka; Ime novodefinisanog tipa se dalje može koristiti kao svaki regularan tip jezika C Ukoliko se radi dodela ili poređenje dva korisnički definisana tipa, koristi se strukturna ekvivalencija tipova, osim kod struktura i unija, kod kojih važi ekvivalencija po imenu svaka neimenovana struktura je poseban tip, čak i u slučaju da su sva polja dve strukture međusobno identična Deklaracije: typedef int CeoBroj; typedef int *PokazivacNaCeoBroj; typedef int NizCelihBrojeva[100]; CeoBroj *pokaza; PokazivacNaCeoBroj pokazb; NizCelihBrojeva niz; Dodele: A) pokaza = pokazb; u redu, jer su pokaza i pokazb pokazivači na isti tip (int*) B) pokaza = niz; u redu, jer identifikator niz je konstantan pokazivač na početak niza C) niz = pokaza; nije u redu, jer želimo dodelu vrednosti konstantnom pokazivaču 5/38 6/38 Zadatak 1 Zadatak 2 kompleksni brojevi (C104) Definicije struktura: struct { int x,y; zapisa; struct { int x,y; zapisb, zapisc; Date definicije su strukturno ekvivalentne, ali zbog ekvivalencije struktura po imenu na jeziku C, druga dodela vrednosti neće biti ispravna: D) zapisb = zapisc; u redu, promenljive iste strkuture E) zapisa = zapisb; nije u redu, promenljive definisane za različite strukture (po imenu) 7/38 Napisati program u programskom jeziku C za rad sa kompleksnim brojevima. Preko argumenata programa zadaju se dva kompleksna broja i to tako da se prvo navodi realan deo prvog broja, zatim imaginarni deo prvog broja, zatim realni i imaginarni deo drugog kompleksnog broja, respektivno. Ako korisnik ne unese tačan broj argumenata potrebno je prekinuti izvršavanje programa. Takođe treba napisati potprogram koji proverava da li znakovni niz sadrži broj i pomoću tog potprograma treba ispitati da li su svi navedeni argumenti zaista brojevi. Ako bar jedan argument nije broj, potrebno je prekinuti izvršavanje programa. Program treba da sadrži i potprogram za sabiranje dva kompleksna broja koji vraća kompleksni broj kao rezultat kao i potprogram za konjugovanje kompleksnog broja koji nema povratnu vrednost. Glavni program treba da na standardnom izlazu ispiše sve argumente programa, zbir kompleksnih brojeva i konjugovane vrednosti oba kompleksna broja. Ispis treba vršiti pozivom odgovarajućeg potprograma koji takođe treba napisati. Kompleksne brojeve predstaviti strukturom. 8/38
Zadatak 2 complex.h Zadatak 2 complex.c /* complex.h */ #define TRUE 1 #define FALSE 0 /* istovremeno se definise struktura sa imenom (SComplex) i tip podatka (TComplex) */ /* complex.c */ #include <stdio.h> #include "complex.h" TComplex Zbir(TComplex z1, TComplex z2) { TComplex cplx; cplx.re = z1.re + z2.re; cplx.im = z1.im + z2.im; return cplx; typedef struct SComplex { double Re, Im; TComplex; void Konjug(TComplex *pz) { pz->im = - pz->im; TComplex Zbir(TComplex z1, TComplex z2); void Konjug(TComplex *pz); void Ispis(TComplex z); void Ispis(TComplex z) { if (z.im >= 0) printf("%5.4f + j%5.4f", z.re, z.im); else printf("%5.4f - j%5.4f", z.re, -z.im); 9/38 10/38 Zadatak 2 glavni program Zadatak 2 /* glavni.c */ #include <complex.h> #include<stdio.h> #include<ctype.h> #include<stdlib.h> int DaLiJeBroj(const char *pstr) { int btacka = FALSE; if (*pstr=='+' *pstr=='-') ++pstr; while(*pstr) { if (!isdigit(*pstr) && *pstr!='.') return FALSE; if (*pstr == '.') { if (btacka) return FALSE; btacka = TRUE; pstr++; return TRUE; int main(int argc, char *argv[]) { TComplex z1, z2, zbir; int i; if (argc!= 5) { printf("unesite tacno cetiri argumenta programa!\n"); return -1; printf("argumenti su sledeci:\n"); for (i=1; i < argc; i++) printf("%d %s\n", i, argv[i]); for (i=1; i < argc; i++) { if (!DaLiJeBroj(argv[i])) { printf("uneli ste argument koji nije broj!\n"); return -1; 11/38 12/38
Zadatak 2 z1.re = atof(argv[1]); z1.im = atof(argv[2]); z2.re = atof(argv[3]); z2.im = atof(argv[4]); printf("\nprvi broj: "); Ispis(z1); printf("\ndrugi broj: "); Ispis(z2); zbir = Zbir(z1, z2); Konjug(&z1); Konjug(&z2); printf("\nzbir: "); Ispis(zbir); printf("\nkonjug(z1): "); Ispis(z1); printf("\nkonjug(z2): "); Ispis(z2); return 0; Ulančana lista Dinamička struktura podataka pogodna za predstavljanje nizova podataka pojedinačni podaci (elementi liste) se dinamički stvaraju i uništavaju elementi liste se nalaze na proizvoljnim mestima u memoriji i međusobno se povezuju pokazivačima Definicija jednog elementa liste sadrži informacioni deo i pokazivač na sledeći element: typedef struct element { int broj; struct element* sled ; Element; Omogućava efikasnu manipulaciju elementima, ali je pristup elementima sekvencijalan 5 3 15 9-2 13/38 15/38 liste (7.6) Sastaviti na jeziku C paket funkcija za rad sa jednostruko ulančanim listama brojeva koji sadrži funkcije za: odredjivanje broja elemenata liste ispisivanje liste preko glavnog izlaza dodavanje broja na početak liste dodavanje broja na kraj liste čitanje liste preko glavnog ulaza dodajući brojeve na početak liste čitanje liste preko glavnog ulaza dodajući brojeve na kraj liste umetanje broja u uredjenu listu brisanje svih elemenata liste izostavljanje iz liste svako pojavljivanje datog broja Sastaviti na jeziku C glavni program za proveru ispravnosti rada gornjih funkcija. Ostvariti gornji paket i pomoću rekurzivnih funkcija. - rešenje /* lista.h - Deklaracije paketa funkcija za obradu lista.*/ /*Element liste*/ typedef struct elem { int broj; struct elem *sled; Elem; /* Broj elemenata liste.*/ int duz (Elem *lst); /* Ispisivanje liste. */ void pisi (Elem *lst); /* Dodavanje na pocetak i na kraj liste. */ Elem *na_pocetak (Elem *lst, int b); Elem *na_kraj (Elem *lst, int b); /* Citanje liste sa dodavanjem na pocetak i na kraj liste. */ Elem *citaj1 (int n); Elem *citaj2 (int n); /* Umetanje u uredjenu listu. */ Elem *umetni (Elem *lst, int b); /* Brisanje svih elemenata liste. */ void brisi (Elem *lst); /* Izostavljanje svakog pojavljivanja. */ Elem *izostavi (Elem *lst, int b); 14/38 16/38
/* lista1.c - Definicije funkcija za obradu lista (iterativno). */ #include "lista.h" #include <stdio.h> #include <stdlib.h> /* Broj elemenata liste. */ int duz (Elem *lst) { int n = 0; while (lst) { n++; lst = lst -> sled; return n; /* Ispisivanje liste. */ void pisi (Elem *lst) { while (lst) { printf ("%d ", lst->broj); lst = lst -> sled; /* Citanje liste stavljajuci brojeve na pocetak. */ Elem *citaj1 (int n) { Elem *prvi = NULL; int i; for (i=0; i<n; i++) { scanf ("%d", &novi->broj); novi->sled = prvi; prvi = novi; return prvi; /* Citanje liste stavljajuci brojeve na kraj. */ Elem *citaj2 (int n) { Elem *prvi = NULL, *posl = NULL; int i; for (i=0; i<n; i++) { scanf ("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; return prvi; 17/38 19/38 /* Dodavanje jednog elementa na pocetak. */ Elem *na_pocetak (Elem *lst, int b) { novi->broj = b; novi->sled = lst; return novi; /* Dodavanje jednog elementa na kraj. */ Elem *na_kraj (Elem *lst, int b) { novi->broj = b; novi->sled = NULL; if (!lst) return novi; Elem *tek = lst; while (tek->sled) tek = tek->sled; tek->sled = novi; /* Umetanje u uredjenu listu. */ Elem *umetni (Elem *lst, int b) { Elem *tek = lst, *pret = NULL, *novi; while (tek && tek->broj < b) { pret = tek; tek = tek->sled; novi = malloc (sizeof(elem)); novi->broj = b; novi->sled = tek; if (!pret) lst = novi; else pret->sled = novi; /* Brisanje svih elemenata liste. */ void brisi (Elem *lst) { while (lst) { Elem *stari = lst; lst = lst->sled; free (stari); 18/38 20/38
glavni program /* Izostavljanje svakog pojavljivanja. */ Elem *izostavi (Elem *lst, int b){ Elem *tek = lst, *pret = NULL; while (tek) if (tek->broj!= b) { pret = tek; tek = tek->sled; Elem *stari = tek; tek = tek->sled; if (!pret) lst = tek; else pret->sled = tek; free (stari); /* listat.c - Ispitivanje paketa funkcija za obradu lista. */ #include "lista.h" #include <stdio.h> void main () { Elem *lst = NULL; int kraj = 0, izbor, broj, n; while (!kraj) { printf ("\n1. Dodavanje broja na pocetak liste\n" "2. Dodavanje broja na kraj liste\n" "3. Umetanje broja u uredjenu listu\n" "4. Izostavljanje broja iz liste\n" "5. Brisanje svih elemenata liste\n" "6. Citanje uz obrtanje redosleda brojeva\n" "7. Citanje uz cuvanje redosleda brojeva\n" "8. Odredjivanje duzine liste\n" "9. Ispisivanje liste\n" "0. Zavrsetak rada\n\n" "Vas izbor? " ); scanf ("%d", &izbor); 21/38 22/38 switch (izbor) { case 1: case 2: case 3: case 4: printf ("Broj?"); scanf ("%d", &broj); switch (izbor) { case 1: /* Dodavanje broja na pocetak liste: */ lst = na_pocetak (lst, broj); break; case 2: /* Dodavanje broja na kraj liste: */ lst = na_kraj (lst, broj); break; case 3: /* Umetanje broja u uredjenu listu: */ lst = umetni (lst, broj); break; case 4: /* Izostavljanje broja iz liste: */ lst = izostavi (lst, broj); break; break; case 5: /* Brisanje svih elemenata liste: */ brisi (lst); lst = NULL; break; case 6: case 7: /* Citanje liste: */ printf ("Duzina? "); scanf ("%d", &n); printf ("Elementi? "); brisi (lst); switch (izbor) { case 6: /* uz obrtanje redosleda brojeva: */ lst = citaj1 (n); break; case 7: /* uz cuvanje redosleda brojeva: */ lst = citaj2 (n); break; break; case 8: /* Odredjivanje duzine liste: */ printf ("Duzina= %d\n", duz (lst)); break; case 9: /* Ispisivanje liste: */ printf ("Lista= "); pisi (lst); putchar ('\n'); break; case 0: /* Zavrsetak rada: */ kraj = 1; break; default: /* Pogresan izbor: */ printf ("*** Neozvoljeni izbor! ***\a\n"); break; 23/38 24/38
rekurzivna implementacija U nastavku je dato nekoliko primera rekurzivne implementacije funkcija za rad sa listama Rekurzivna rešenja za svaki novi poziv potroše dodatnu količinu memorije, tako da je bolje opredeliti se za iterativna rešenja /*vršimo rekurzivne pozive sve dok ne dođemo do kraja liste i prilikom svakog poziva ubrojimo obrađeni element*/ int duz (Elem *lst) { return lst? duz (lst->sled) + 1 : 0; /*krećemo se kroz listu rekurzivnim pozivima i ispisujemo sadržaj */ void pisi (Elem *lst) { if (lst) { printf ("%d ", lst->broj); pisi (lst->sled); /*ne koristi se rekurzija nema potrebe*/ Elem *na_pocetak (Elem *lst, int b) { novi->broj = b; novi->sled = lst; return novi; /* krećemo se kroz listu rekurzivnim pozivima dok god ne dođemo do kraja, a zatim radimo ponovno ulančavanje od kraja liste ka početku */ Elem *na_kraj (Elem *lst, int b) { if (!lst) { lst = malloc (sizeof(elem)); lst->broj = b; lst->sled = NULL; else lst->sled = na_kraj (lst->sled, b); 25/38 26/38 Elem *citaj1 (int n) { if (n == 0) return NULL; novi->sled = citaj1 (n - 1); scanf ("%d", &novi->broj); return novi; Elem *citaj2 (int n) { if (n == 0) return NULL; scanf ("%d", &novi->broj); novi->sled = citaj2 (n - 1); return novi; /*krećemo se do kraja, a zatim brišemo elemente od kraja void brisi (Elem *lst) { if (lst) { brisi (lst->sled); free (lst); Elem *umetni (Elem *lst, int b) { if (!lst lst->broj >= b) { novi->broj = b; novi->sled = lst; return novi; lst->sled = umetni (lst->sled, b); Elem *izostavi (Elem *lst, int b){ if (lst) { if (lst->broj!= b) { lst->sled = izostavi (lst->sled, b); Elem *stari = lst; lst = izostavi (lst->sled, b); free (stari); 27/38 28/38
Zadatak 4 obrtanje, sortiranje, fuzija (7.7) Napisati program na programskom jeziku C koji učitava dve liste celih brojeva, obrće ih, sortira, i potom spaja u jednu sortiranu listu. Za obrtanje, uređivanje i spajanje listi napisati potprograme. Koristiti definiciju liste i operacija nad listama iz prethodnog zadatka. Zadatak 4 - obrtanje /* lista3.c - Jos funkcija za obradu lista. */ #include "lista.h" #include <stdio.h> /* Obrtanje redosleda elemenata. */ Elem *obrni (Elem *lst) { Elem *tek = lst, *pret = NULL; while (tek) { Elem *sled = tek->sled; tek->sled = pret; pret = tek; tek = sled; return pret; 29/38 30/38 Zadatak 4 - sortiranje Zadatak 4 - fuzija Za uređivanje liste se u ovom slučaju koristi klasični metod sortiranja putem selekcije Prilikom zamene, nije potrebno prevezivati elemente liste, već je dovoljno uraditi zamenu informacionog sadržaja /* Uredjivanje liste. */ void uredi (Elem *lst) { Elem *i, *j; for (i=lst; i; i=i->sled) for (j=i->sled; j; j=j->sled) if (j->broj < i->broj) { int b = i->broj; i->broj = j->broj; j->broj = b; 31/38 /* Fuzija dve uredjene liste. */ Elem *fuzija (Elem *lst1, Elem *lst2) { Elem *lst = NULL, *posl=null, *tek; while (lst1 && lst2) { if (lst1->broj < lst2->broj) { tek = lst1; lst1 = lst1->sled; tek = lst2; lst2 = lst2->sled; tek->sled = NULL; if (!lst) lst = tek; else posl->sled = tek; posl = tek; if (lst1) { if (!lst) lst = lst1; else posl->sled = lst1; if (!lst) lst = lst2; else posl->sled = lst2; 32/38
Zadatak 4 main() Zadatak 5 unije (C2008-A1) void main () { while (1) { Elem *lst1, *lst2, *lst3; int n; printf ("Duzina prve liste? "); scanf ("%d", &n); if (n <0) break; printf ("Elementi prve liste? "); lst1 = citaj2 (n); if (n == 0) putchar ('\n'); printf ("Duzina druge liste? "); scanf ("%d", &n); if (n <0) break; printf ("Elementi druge liste? "); lst2 = citaj2 (n); if (n == 0) putchar ('\n'); lst1 = obrni (lst1); printf ("Obrnuta prva lista: "); pisi (lst1); putchar ('\n'); lst2 = obrni (lst2); printf ("Obrnuta druga lista: "); pisi (lst2); putchar ('\n'); uredi (lst1); printf ("Uredjena prva lista: "); pisi (lst1); putchar ('\n'); uredi (lst2); printf ("Uredjena druga lista: "); pisi (lst2); putchar ('\n'); lst3 = fuzija (lst1, lst2); printf ("Fuzija obe liste: "); pisi (lst3); printf ("\n\n"); brisi (lst3); Šta ispisuje sledeći program na programskom jeziku C, ukoliko je pozvan sa prog 3 5 7 8? #include <stdio.h> #include <stdlib.h> union { struct { int a,b; c; struct { int a,b,c; d; e; void main(int argc, char* argv[]) { int s=atoi(argv[1]), br = atoi(argv[2]); for(e.c.a=0;e.c.a<br;e.c.a++) for(e.d.a=0;e.d.a<5;e.d.a++) for(e.c.a=0;e.c.a<5;e.c.a++) for(e.c.b=0;e.c.b<5;e.c.b++) s++; printf("%d", s); Odgovori: A) 30 B) 28 C) 628 33/38 34/38 Unije Zadatak 5 - rešenje Struktuirani tip koji omogućava da se u isti memorijski prostor smeštaju podaci različitih tipova u različitim vremenskim intervalima u jednom trenutku samo jedan podatak je validan Naredba union ima istu sintaksu kao struct union { int i; double d; char *c; u; Međutim, za razliku od strukture, veličina unije je određena najvećom komponentom s.i s.d sizeof(s) s.c u.i u.d u.c sizeof(u) 35/38 S obzirom na to da je kod elemenata unije memorijski prostor deljen, polja a i b strukture c dele iste lokacije sa poljima a i b strukture d svaka promena nad poljima a i b strukture c istovremeno predstavlja i promenu nad poljima a i b strukture d i obrnuto Na početku promenljiva s dobija vrednost 3, a br 5. spoljni ciklus (e.c.a) se na prvi pogled ponavlja 5 puta medjutim, već naredni unutrašnji ciklus (e.d.a) se vrši nad istim podatkom, tako da kada on bude završen, e.d.a će imati vrednost 5, a to automatski važi i za e.c.a sledeći for ciklus se odvija ponovo nad e.c.a na osnovu prethodnog zaključka, kada on bude završen, automatski će biti ispunjen uslov za prekid prva dva ciklusa "koristan efekat" u programu imaju naredna dva ugneždena ciklusa ukupan broj izvršavanja operacije s++ je zbog toga 25 početna vrednost s bila je 3, konačna vrednost u trenutku ispisivanja će biti 28 Odgovor: B 36/38
Zadatak 6 Zadatak 6 Dat je deo programskog koda na jeziku C za rad sa dvostruko ulančanim listama celih brojeva. Funkcija posle_tek treba da ubaci novi element iza tekućeg elementa dvostruko ulančane liste. Lista ima zaglavlje koje se sastoji od pokazivača na prvi i pokazivača na tekući element. Šta treba da stoji na mestu ##### da bi ova funkcija ispravno obavljala tu operaciju? #include <stdlib.h> typedef struct elem { void *pod; struct elem *sled, *pret; Elem; typedef struct { Elem *prvi, *tek; Lista; void posle_tek (Lista *plst, void *p) { Elem *novi; if( plst->tek == NULL ) return; novi = (Elem *)malloc (sizeof(elem)); novi->pod = p; novi->pret = plst->tek; novi->sled = plst->tek->sled; if(novi->sled) ##### plst->tek->sled = novi; plst->tek = novi; Odgovori: A) plst->tek->sled->pret = novi; B) novi->sled->pret = novi; C) (*(*plst).tek).pret = novi; 37/38 38/38