Skip to content

Rešeni zadaci iz jezika C sa rokova iz Programiranja 2 na ETF-u

Notifications You must be signed in to change notification settings

vomindoraan/P2-Rokovi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

P2-Rokovi

Rešeni zadaci iz jezika C sa rokova iz Programiranja 2 na ETF-u

Skraćenice

Motivacija

U svim ispitnim zadacima gde se javlja rukovanje dinamičkom memorijom ili datotekama (a to su maltene svi zadaci) potrebno je pisati iste delove koda za proveru uspešnosti alokacije ili otvaranja datoteke iznova i iznova. To izgleda npr. ovako:

Elem *p = malloc(sizeof *p);
if (!p) {
    printf("Neuspesna alokacija\n");
    exit(1);
}
FILE *fp = fopen("unos.txt", "r");
if (!fp) {
    printf("Neuspesno otvaranje fajla\n");
    exit(2);
}

Pošto su ovi delovi uvek isti, može se značajno uštedeti na pisanju ako se taj kod izvuče u makroe. Na taj način, umesto pisanja if naredbi na svakom mestu gde se alocira memorija ili otvara datoteka (što može biti i do 5–6 puta po zadatku), dobija se ovako nešto:

Elem *p = malloc(sizeof *p);
CHECK_ALLOC(p);

ili, ukoliko je p ranije definisano, još kraće: CHECK_ALLOC(p = malloc(sizeof *p));. Naravno, na kraju se memorija i dalje mora osloboditi s free(p).

Implementacija

Pomenuti makroi se mogu realizovati na sledeći način:

#define CHECK_ALLOC(p) if (!(p)) puts("Neuspesna alokacija"), exit(1)
#define CHECK_FILE(f)  if (!(f)) perror(NULL), exit(2)

Ako je ostatak koda na srpskom, mogu se nazvati i PROV_MEM i PROV_DAT, ili bilo kako drugačije. Manje poznate funkcije koje se ovde koriste su objašnjene ispod.

Na ovaj način, kada pretprocesor uradi tekstualnu zamenu, naredba CHECK_ALLOC(p = malloc(sizeof *p)); postane if (!(p = malloc(sizeof *p))) puts("Neuspesna alokacija"), exit(1); što je ekvivalentno velikom if bloku odozgo; odnosno:

  1. pokuša se alokacija bloka memorije date veličine;
  2. rezultat se dodeli u pokazivačku promenljivu;
  3. ako je rezultat nula, ispisuje se poruka i prekida se program.

Isto važi i za makro CHECK_FILE, samo je tu u pitanju otvaranje datoteke umesto alokacija. Primetiti da su u makroima zagrade if (!(p)) veoma bitne jer bi bez njih tekstualna zamena dala neispravan kod.

ASSIGN makro

Otvaranje datoteka se još više može uprostiti ako se uvede makro:

#define ASSIGN(p, f, m) if (!((p) = fopen(f, m))) perror(f), exit(1)

Ovaj makro u jednom potezu pozove fopen za dato ime i mod, sačuva rezultat u pokazivač, i proveri da li je kojim slučajem došlo do greške pri otvaranju, a ako jeste prekine program. Koristi se na sledeći način (slično kao funkcija Assign u Paskalu):

FILE *fp;
ASSIGN(fp, "moja_dat.bin", "rb+");

Nedostatak

Mali nedostatak ovih makroa je da se zbog svog oblika ne smeju naći unutar kratke if naredbe (bez { }) neposredno pre else grane. Makro ekspanzija bi u tom slučaju neželjeno izmenila semantiku programa. Primer:

if (uslov)
    CHECK_ALLOC(p = malloc(sizeof *p));
else  // Odnosi se na if u makrou, ne na `if (uslov)`
    puts("Nije uslov");

Mala je verovatnoća da se ovo desi, ali u tom slučaju samo treba pisati vitičaste zagrade oko grana if-a i neće biti problema.

Korisne funkcije

Funkcija puts ispisuje string na standardni izlaz i prelazi u novi red. puts("Neuspesna alokacija"); je, dakle, isto što i printf("Neuspesna alokacija\n");, samo što je malo brže za pisanje, i ne može doći do slučajne greške ako se doslovno ispisuju znaci %, jer puts nema formatiranje.

fputs radi sličnu stvar, samo što umesto na izlaz ispisuje string u datoteku koja se zadaje kao drugi argument. Dakle, fputs("Neuspesna alokacija", stderr) umesto na standardni izlaz ispisuje poruku na izlaz za greške (mada to nije toliko bitno za zadatke), dok fputs("neki string\n", fp) upisuje string u otvorenu datoteku na koju pokazuje fp. Za razliku od puts, ova funkcija ne ispisuje sama po sebi znak za novi red, već on mora biti deo stringa.

Veoma korisna funkcija koja automatski ispisuje odgovarajuću poruku za grešku koja se desila prilikom otvaranja datoteke, npr. „No such file or directory” ili „File already in use”. Umesto praznog pokazivača perror(NULL) se može proslediti string koji će se ispisati zajedno sa porukom, npr. perror("Greska") daje „Greska: File already in use”.

Sve gorenavedene funkcije su iz zaglavlja <stdio.h>.

Ostalo

Potpis funkcije main

Po standardu jezika C postoje dva ispravna potpisa za glavni program:

  1. int main(void) – bez argumenata komandne linije;
  2. int main(int argc, char *argv[]) – sa argumentima komandne linije.

Pritom vredi naglasiti da imena parametara argc i argv nisu ni na koji način posebna i da se oni mogu zvati bilo kako, dokle god su im tipovi int i char*[] (odnosno char**), tim redom. Dakle, i int main(int n, char **a) je sasvim ispravan potpis glavnog programa.

Prema tome, varijante void main() ili, ne daj bože, samo main() nisu ispravne i ne treba tako pisati. Razlozi zašto prevodilac ovo dozvoljava su istorijski (davno, pre standardizacije C-a, se pisalo ovako i na još par načina), ali tome nije mesto u današnjem kodu. Mada iz nekog razloga na Katedri i dalje ovako pišu.

Što se tiče potpisa int main(), i on može proći kao prigodna zamena za 1. varijantu, ali postoji mala razlika: u C-u potpis (void) znači da funkcija ne prima argumente, dok () znači da prima neodređen broj argumenata. Prva varijanta je bolja jer je eksplicitnija.

Povratna vrednost glavnog programa je tipa int zato što preko nje programi javljaju okruženju da li su se uspešno izvršili (0 – uspešno; ≠0 – neuspešno, a sama vrednost predstavlja kod greške). Međutim, return 0; na kraju main-a se ne mora pisati jer se po standardu u toj (i samo u toj) funkciji podrazumeva.

sizeof *p ili sizeof(tip)

Ove dve operacije rade isto (daju veličinu u bajtovima onoga na šta p pokazuje), ali je prvo fleksibilnije i to je preporučeni način pisanja. Primetiti da kada se stavlja *p umesto tip zagrade nisu potrebne.

Alociranje niza od 10 brojeva onda izgleda ovako: int *niz = malloc(10 * sizeof *niz); ili int *niz = calloc(10, sizeof *niz);, dok se matrice formiraju na sledeći način:

int **mat  = calloc(m, sizeof *mat);   // *mat daje tip int*
for (i = 0; i < m; i++) {
    mat[i] = calloc(n, sizeof **mat);  // **mat daje tip int
    for (j = 0; j < n; j++) {
        scanf("%d", &mat[i][j]);
    }
}

About

Rešeni zadaci iz jezika C sa rokova iz Programiranja 2 na ETF-u

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages