This shows you the differences between two versions of the page.
programare-ca:laboratoare:lab13 [2013/01/07 00:01] florin.pop [Exerciţii de Laborator] |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 13. Recapitulare ===== | ||
- | **Responsabili:** | ||
- | * [[florin.pop@cs.pub.ro|Florin Pop]], [[george.popescu@cs.pub.ro|George Popescu]] | ||
- | |||
- | ==== Obiective ==== | ||
- | |||
- | În urma parcurgerii acestui laborator, studentul va fi capabil: | ||
- | * să-şi estimeze gradul de acoperire al cunoştinţelor la Programare; | ||
- | * să-şi facă o privire de ansamblu mai bună asupra noţiunilor învăţate la laborator şi a modului în care acestea sunt legate între ele; | ||
- | |||
- | ==== Exerciţii de Laborator ==== | ||
- | |||
- | * ''[Easy]'' Scrieţi un program care citeşte un număr <tt>n</tt> de la tastatură şi apoi alte <tt>n*n</tt> numere reale, pe care le plasează într-o matrice pătratică, alocată static, de dimensiune <tt>n</tt>. Să se sorteze apoi aceste numere crescător, de la stânga la dreapta şi de sus în jos, fără a folosi alţi vectori sau matrice, şi apoi să se afişeze matricea sortată. | ||
- | |||
- | * ''[Medium]'' Scrieţi un program care defineşte un vector de 10 numere întregi, pe care îl iniţializează apoi cu numere de la 0 la 9 şi apoi afişează conţinutul memoriei ocupate de vector, octet cu octet, fiecare octet fiind afişat cu două cifre hexazecimale. | ||
- | |||
- | * ''[Medium]'' a) Care este diferenţa dintre următoarele două expresii? Explicaţi concluzia. | ||
- | <code c> | ||
- | int expr = (a + b)*(c - d); | ||
- | </code> | ||
- | <code c> | ||
- | int expr = ((a + b))*((c - d)); | ||
- | </code> | ||
- | |||
- | b) Care este diferenţa dintre următoarele două expresii? Explicaţi concluzia. | ||
- | <code c> | ||
- | printf("Hello there, %s!\n", "student"); | ||
- | </code> | ||
- | <code c> | ||
- | printf(("Hello there, %s!\n", "student")); | ||
- | </code> | ||
- | |||
- | * ''[Hard]'' Fie următoarea structură: | ||
- | <code c> | ||
- | struct test_struct { | ||
- | char a; | ||
- | short b; | ||
- | char c; | ||
- | int d; | ||
- | }; | ||
- | </code> | ||
- | |||
- | Scrieţi un program care defineşte această structură şi o variabilă de acest tip şi care afişează diferenţa dintre dimensiunea totală a structurii (calculată cu operatorul <tt>sizeof</tt>) şi suma dimensiunilor fiecărui câmp în parte al structurii. Explicaţi rezultatul obţinut. | ||
- | |||
- | '''SOLUŢIE:''' | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | struct test_struct { | ||
- | char a; | ||
- | short b; | ||
- | char c; | ||
- | int d; | ||
- | };// __attribute__((packed)); | ||
- | //decomentati linia anterioara pentru a instrui compilatorul sa nu mai puna | ||
- | // "padding" in structuri. | ||
- | |||
- | int main() { | ||
- | struct test_struct t; | ||
- | void *start, *offset; | ||
- | | ||
- | printf("sizeof(struct): %d\n", sizeof(t)); | ||
- | printf("sum(sizeof(campuri)): %d\n", | ||
- | sizeof(t.a) + sizeof(t.b) + sizeof(t.c) + sizeof(t.d)); | ||
- | | ||
- | //Bonus, afisarea pozitiilor fiecarui camp in structura. Ce se afiseaza | ||
- | //daca se mentine si atributul __attribute__((packed)) in declaratia | ||
- | //structurii? | ||
- | start = &t; | ||
- | offset = &t.a; | ||
- | printf("Offset a: %d\n", offset - start); | ||
- | | ||
- | offset = &t.b; | ||
- | printf("Offset b: %d\n", offset - start); | ||
- | | ||
- | offset = &t.c; | ||
- | printf("Offset c: %d\n", offset - start); | ||
- | | ||
- | offset = &t.d; | ||
- | printf("Offset d: %d\n", offset - start); | ||
- | | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | * ''[Medium]'' Definiţi macro-ul <tt>for_each(vect, n, val)</tt>, care să reprezinte antetul unei bucle de iteraţie prin elementele de tip <tt>int</tt> ale vectorului <tt>vect</tt>. <tt>n</tt> reprezintă numărul de elemente ale vectorului şi <tt>val</tt> numele unei variabile care să conţină valoarea curentă din iteraţie. Un exemplu de funcţie care foloseşte acest macro arată astfel: | ||
- | <code c> | ||
- | void afiseaza(int *v, int n) { | ||
- | int crt_val; | ||
- | |||
- | for_each(v, n, crt_val) { | ||
- | printf("%d ", crt_val); | ||
- | } | ||
- | printf("\n"); | ||
- | } | ||
- | </code> | ||
- | |||
- | * ''[Medium]'' Implementaţi funcţiile: | ||
- | <code c> | ||
- | int max(int a, int b); | ||
- | int min(int a, int b); | ||
- | </code> | ||
- | care calculează maximul, respectiv minimul dintre două numere fără a folosi nici o comparaţie (instrucţiune <tt>if</tt> sau operatorul ternar). Scrieţi apoi un program care primeşte două numere întregi ca parametri '''în linia de comandă''' şi afişează maximul şi minimul folosind cele două funcţii definite mai sus. | ||
- | |||
- | * ''[Hard]'' Fie programul C simplu de mai jos: | ||
- | <code c> | ||
- | int func() { | ||
- | return 1 + 2 + 3 + 4; | ||
- | } | ||
- | |||
- | int main() { | ||
- | return func(10, 20, 30, 40); | ||
- | } | ||
- | </code> | ||
- | Încercaţi să-l compilaţi. Ce observaţi? Ce explicaţie aveţi pentru acest comportament? | ||
- | |||
- | * ''[Medium]'' Implementaţi funcţiile: | ||
- | <code c> | ||
- | int sort(void * , int (*f) (void *, void*)); | ||
- | //funcţie care sortează un vector si foloseste drept comparator functia f. | ||
- | |||
- | int comparator (void * a, void * b); | ||
- | //funcţie care întoarce un număr negativ dacă a<b, 0 pentru a==b, un număr pozitiv pentru a>b, particularizată pentru numere întregi. | ||
- | </code> | ||
- | |||
- | Scrieţi apoi un program care primeşte ca parametru al liniei de comandă un nume de fişier care este de forma: | ||
- | |||
- | N | ||
- | |||
- | A1 A2 A3 ... An | ||
- | |||
- | unde: | ||
- | |||
- | N-> numărul de elemente ale vectorului | ||
- | |||
- | Ai -> numere întregi. | ||
- | |||
- | '''SOLUŢIE:''' | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | //functia de sortare generica | ||
- | int sort(void * v, int n, int size, int (*f) (void *, void*)) { | ||
- | int i,j; | ||
- | void *aux = malloc(size); | ||
- | for (i = 0; i < n; i++) { | ||
- | for (j = 0; j < n; j++) { | ||
- | //incrementarea pointerilor void* se face cu 1 | ||
- | if (f(v + i*size, v + j*size) > 0) { | ||
- | memcpy(aux, v + i*size, size); | ||
- | memcpy(v + i*size, v + j*size, size); | ||
- | memcpy(v + j*size, aux, size); | ||
- | } | ||
- | } | ||
- | } | ||
- | | ||
- | } | ||
- | |||
- | //functia care compara doua elemente date prin pointeri void* | ||
- | int comparator (void * a, void * b) { | ||
- | return *((int*)a) - *((int*)b); | ||
- | } | ||
- | |||
- | |||
- | int main(int argc, char *argv[]) { | ||
- | if (argc < 2) | ||
- | return 1; | ||
- | | ||
- | FILE * f = fopen(argv[1], "r"); | ||
- | | ||
- | //citire din fisier si alocare de memorie | ||
- | int i, *v, n; | ||
- | fscanf(f, "%d", &n); | ||
- | v = (int*)malloc(n * sizeof(int)); | ||
- | | ||
- | for (i = 0; i < n; i++) { | ||
- | fscanf(f, "%d", &v[i]); | ||
- | } | ||
- | | ||
- | fclose(f); | ||
- | | ||
- | //afisare date initiale | ||
- | for (i = 0; i < n; i++) { | ||
- | printf("%d ", v[i]); | ||
- | } | ||
- | printf("\n"); | ||
- | | ||
- | //sortare si afisare dupa sortare | ||
- | sort(v, n, sizeof(int), comparator); | ||
- | | ||
- | for (i = 0; i < n; i++) { | ||
- | printf("%d ", v[i]); | ||
- | } | ||
- | } | ||
- | </code> | ||
- | |||
- | * ''[Hardcore]'' Într-un fişier MP3 datele legate de titlul melodiei, artist, album, etc sunt stocate conform cu standardul ID3, într-o structură numită ''tag ID3''. Această structură ocupă ultimii 128 octeţi din fişier (vezi descrierea detaliată în continuare). Prezenţa sa este determinată de primii 3 octeţi din tag. Astfel, dacă primii 3 octeţi din cei 128 conţin caracterele <tt>TAG</tt> atunci tagul ID3 este prezent. Altfel, se consideră că este absent. | ||
- | |||
- | Câmpurile unui tag ID3 sunt reprezentate în fişier pe ultimii 128 de octeţi, după cum urmează: | ||
- | |||
- | ^ Field ^ Length ^ Description | ||
- | | header | 3 | "TAG" | ||
- | | title | 30| 30 characters of the title | ||
- | | artist | 30| 30 characters of the artist name | ||
- | | album | 30| 30 characters of the album name | ||
- | | year | 4 | A four-digit year | ||
- | | comment| 28 or 30 | The comment. | ||
- | | zero-byte | 1 | If a track number is stored, this byte contains a binary 0. | ||
- | | track | 1 | The number of the track on the album, or 0. Invalid, if previous byte is not a binary 0. | ||
- | | genre | 1 | Index in a list of genres, or 255 | ||
- | |||
- | Se cere să realizaţi un program care, primind în linia de comandă o serie de nume de fişiere mp3 să afişeze (în cazul în care există): | ||
- | # numele artistului | ||
- | # titlul melodiei | ||
- | # titlul albumului | ||
- | # anul înregistrării | ||
- | # genul melodiei | ||
- | Numele genurilor vor fi citite din fisierul <tt>genres.dat</tt> care poate fi găsit împreună cu două fişiere de test la [http://www.dump.ro/download.php?id=71093&act=IDLzolIxHy1re3g6 această adresă]. Fişierul <tt>genres.dat</tt> conţine un număr de linii, fiecare linie conţinând indexul genului şi numele lui. | ||
- | |||
- | * ''[Medium]'' Scrieţi un program care primeşte ca parametru un nume de fişier şi îi afişează pe ecran conţinutul în hexazecimal, pe linii de câte 64 de caractere (asemănător cu utilitarul <tt>hexdump</tt> din Linux). | ||
- | |||
- | * ''[Medium]'' Scrieţi un program care permite căutarea unui cuvânt sau a unei măşti într-un text. Masca poate conţine caractere (care vor fi verificate ca atare) sau caracterul <tt>?</tt> cu semnificaţia că poate fi înlocuit cu orice caracter. Textul se va citi dintr-un fişier al cărui nume va fi trimis ca parametru în linia de comandă. Se vor afişa cuvintele găsite şi linia pe care au fost găsite. | ||
- | |||
- | |||
- | ==== Teste recapitulative ==== | ||
- | |||
- | === Testul 1 === | ||
- | |||
- | |||
- | 1. (task1.c) Fie funcţiile definite astfel: | ||
- | <code c> | ||
- | int functie(void); | ||
- | int functie(); | ||
- | </code> | ||
- | |||
- | Sunt cele două definiţii echivalente? Scrieţi un program C care să justifice răspunsul dat. | ||
- | |||
- | 2. (task2.c) Scrieţi un program care să afişeze propriul cod sursă. | ||
- | |||
- | 3. (task3.c) Rezolvaţi următoarele cerinţe: | ||
- | |||
- | * 3.1 Definiţi structura ''Numar_Complex'', cu câmpurile parte reală, parte imaginară. | ||
- | |||
- | * 3.2. Definiţi funcţia ''Numar_Complex* citire (char *filename,int *n)'', care să citească din fişierul dat ca parametru numărul natural n, după care să aloce un vector de n numere complexe, care se vor citi apoi de pe următoarele n linii ale fişierului. | ||
- | |||
- | Formatul fisierului: | ||
- | |||
- | n | ||
- | x1 y1 | ||
- | x2 y2 | ||
- | ... | ||
- | xn yn | ||
- | |||
- | * 3.3 Scrieţi un program care citeşte un vector de n numere complexe folosind funcţia de la punctul precedent, şi apoi afişează numărul de modul maxim şi poziţia acestuia în vector. | ||
- | |||
- | 4. (task4.c) Scrieţi un program care poate primi (ca argumente în linia de comandă) opţiunile “-a” şi “-p”. Programul primeşte ca prim parametru în linia de comandă un număr întreg n. | ||
- | |||
- | *Opţiunea “-a” va avea ca efect afişarea adresei variabilei din program în care se reţine numărul. | ||
- | *Opţiunea “-p” va avea ca efect afişarea pătratului numarului. Pentru a calcula pătratul numărului se va folosi o macroinscructiune. | ||
- | *Se permite rularea executabilului cu ambele opţiuni, caz în care se va afişa atât adresa variabilei cât şi pătratul valorii. La rularea executabilului fără opţiuni, nu se va afiţa nimic. | ||
- | *Se va afişa un mesaj de eroare în oricare dintre cazurile: lipsa parametrului n, parametrii nu sunt cei specificaţi mai sus, număr prea mare de parametri. | ||
- | |||
- | 5. (task5.c) Scrieţi un program care interschimbă două variabile x şi y, fără a folosi o variabilă auxiliară. | ||
- | |||
- | 6. Modificaţi următorul fişier makefile, astfel încât executabilul de la problema 4 să se numească task6. | ||
- | <code c> | ||
- | #makefile | ||
- | |||
- | CC=gcc | ||
- | CFLAGS=-Wall | ||
- | |||
- | all: task1 task2 task3 task4 task5 | ||
- | |||
- | task1: task1.c | ||
- | task2: task2.c | ||
- | task3: task3.c | ||
- | task4: task4.c | ||
- | task5: task5.c | ||
- | </code> | ||
- | |||
- | === Testul 2 === | ||
- | |||
- | 1. (task1.c) De ce urmatorul program nu afişează rezultatul corect/aşteptat? | ||
- | |||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main() | ||
- | { | ||
- | float f=0.0f; | ||
- | int i; | ||
- | for(i=0;i<10;i++) | ||
- | f = f + 0.1f; | ||
- | | ||
- | if(f == 1.0f) | ||
- | printf("f is 1.0 \n"); | ||
- | else | ||
- | printf("f is NOT 1.0\n"); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | 2. (task2.c) Scrieţi un program care să afişeze câţi biţi dintr-un număr reprezentat pe 4 octeţi sunt 1. | ||
- | |||
- | 3. (task3.c) Rezolvaţi următoarele cerinţe: | ||
- | |||
- | * 3.1 Definiţi structura Numar_Real, cu câmpurile parte întreagă, parte fracţionară. | ||
- | |||
- | * 3.2 Definiţi funcţia ''Numar_Real* citire(char *filename,int *n)'', care să citească din fişierul dat ca parametru dimensiunea vectorului v, să îl aloce dinamic, apoi să citeasca n numere reale, şi să le reţină într-un vector, sub formă de structuri definite ca la punctul precedent. | ||
- | |||
- | Formatul fişierului: | ||
- | |||
- | n | ||
- | nr_1 | ||
- | nr_2 | ||
- | ... | ||
- | nr_n | ||
- | |||
- | * 3.3 Scrieţi un program care să testeze funcţia de citire de la punctul precedent, afişând numerele din vector în ordinea crescătoare a părţii fracţionare. Afişarea se va face în forma: | ||
- | i1 f1 | ||
- | i2 f2 | ||
- | ... | ||
- | in fn, | ||
- | unde i reprezintă partea întreagă, iar f partea fracţionară. | ||
- | |||
- | 4. (task4.c) Scrieţi un program care primeşte (ca argumente în linia de comandă) două numere ''a'' şi ''b'', şi poate primi una dintre opţiunile: “-m” sau “-f”. | ||
- | * Veţi defini un macro care calculează minimul dintre a şi b, precum şi o funcţie cu acelaşi scop: <tt>int minim(int a, int b)</tt>. | ||
- | * Dacă este apelat cu optiunea “-m”, programul va afisa minimul dintre a si b, folosind macrodefinitia. | ||
- | * Dacă este apelat cu optiunea “-f”, programul va afişa adresa funcţiei <tt>minim</tt>. Dacă este apelat cu ambele optiuni, vor fi afişate ambele informaţii. | ||
- | * Programul va afişa un mesaj de eroare în oricare dintre următoarele cazuri: nu sunt daţi parametrii a şi b (sau unul dintre ei), parametrii nu sunt cei specificaţi mai sus, număr prea mare de parametri. | ||
- | |||
- | 5. (task5.c) Scrieţi un program care afişează caracterul ';' (cod ASCII: 59), fără a folosi '''deloc''' ';' în sursa C. (task5.c) | ||
- | |||
- | 6. Modificaţi următorul fişier makefile, adăugând o regulă ''clean'', care să şteargă fişierele executabile, precum şi fisierele temporare, ale căror nume se termină cu '~'. | ||
- | <code c> | ||
- | #makefile | ||
- | |||
- | CC=gcc | ||
- | CFLAGS=-Wall | ||
- | |||
- | all: task1 task2 task3 task4 task5 | ||
- | |||
- | task1: task1.c | ||
- | task2: task2.c | ||
- | task3: task3.c | ||
- | task4: task4.c | ||
- | task5: task5.c | ||
- | </code> |