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