Differences

This shows you the differences between two versions of the page.

Link to this comparison view

programare:laboratoare:lab07 [2018/11/03 16:48]
darius.neatu [Operatori pe biți în C]
programare:laboratoare:lab07 [2025/10/15 18:31] (current)
darius.neatu [Referinţe]
Line 1: Line 1:
 +===== PCLP Laborator07:​ Prelucrarea şirurilor de caractere. Funcţii. Aplicaţii =====
  
-===== Optimizarea programelor folosind operaţii pe biţi  ===== 
  
-**Responsabili:​** +**Resposabili**
-  * [[neatudarius@gmail.com|Darius Neațu (2018, 2017)]] +  * [[ion_dorinel.filip@cti.pub.ro|Dorinel Filip (2016-2020)]] 
-  * [[rares96cheseli@gmail.com|Rareș Cheșeli ​(2018, 2017)]] +  * [[neatudarius@gmail.com|Darius Neațu (2019-2020)]] 
 +  * [[emil.racec@gmail.com|Emil Racec (2012)]] 
 +  * [[alina.g.simion@gmail.com|Alina Simion (2008)]] 
 +  * [[stefan.bucur@gmail.com|Ştefan Bucur (2006)]]
  
-==== Obiective ====  
  
-În urma parcurgerii acestui laborator, studentul va fi capabil: +==== Obiective ====
-  * să înțeleagă conceptele legate de operații pe biți +
-  * să utilizeze operații pe biți pentru optimizare (când este cazul) +
-  * să înteleagă mai multe despre organizarea datelor în memorie +
-  * să gestioneze mai bine memoria folosită într-un anumit program C +
-  * să implementeze o structură de date in C+
  
-<note warning>​ +În urma parcurgerii acestui laborator studentul va fi capabil:
-    * Laboratorul curent introduce mai multe noțiuni. Pe multe dintre acestea le veți mai întâlni și la alte cursuri din următorii ani, precumIOCLA, CN/CN2, AA/PA, PC, PM, SO. +
-    * Secțiunile ** Studiu de caz ** și ** Probleme de interviu ** nu sunt obligatorii,​ dar recomandăm parcurgerea întregului material pentru o viziune de ansamblu mai bună. Atenție! Problemele de interviu sunt mult peste nivelul așteptat la această materie! +
-</​note>​+
  
-==== Motivație ==== +  * să declare şi să folosească şiruri de caractere 
-În cadrul acestui articol vă vom prezenta câteva metode prin care programele se pot optimiza dacă utilizăm eficient operaţiile ​pe biţi. Un lucru foarte important ​de reținut este că nu întotdeauna putem folosi aceste operații pentru optimizare, iar laboratorul are ca scop ilustrarea câtorva **exemple**,​ pe care le puteți întâlni și pe viitor.+  * să folosească funcţiile de manipulare a şirurilor de caractere din libraria string.h
  
-Veți învăța în anul 2 la Analiza Algoritmilor despre complexitatea unui algoritm. Vom considera momentan că un algoritm este mai rapid decât altul dacă are mai puțini pași (exemplu un for cu n 100 de pași este mai rapid decât un for cu 1000 de pași). ​+==== Noţiuni teoretice ====
  
-În exemplul anterior **performanța** se referă la timp (dacă executăm mai puține instrucțiuni într-un program, ne așteptăm să se termine mai repede). Acest aspect va fi abordat pe larg la materiile AA și PA.  
  
-În acest laborator vom vorbi despre altă metrică de măsurare a performanței unui program, mai exact despre ** memoria folosită ** de un program. 
  
-De ce este **important** și acest aspect? Dacă din punct de vedere al timpului de execuție, sunt situații în care putem aștepta mai mult timp pentru a se termina programul, din punctul de vedere al memoriei folosite avem o limitare exactă. Un exemplu simplu este calculatorul nostru, care are 4GB/​8GB/​16GB. Dacă mașina noastră are X GB RAM, dintre care o parte importantă o ocupă sistemul de operare, asta înseamnă că într-un anumit program nu putem folosi o cantitate nelimitată de RAM (mai multe detalii la CN2, SO). Pentru **simplitate**,​ momentan presupunem că programul nostru nu poate rula pe o mașină cu X GB, dacă are nevoie de mai mult de X GB. +=== Şiruri ​de caractere ===
-  +
-Dacă ajungem într-o astfel de situație în mod evident trebuie să schimbăm ceva, însă de multe ori putem păstra algoritmul și să facem câteva modificări în implementare,​ care exploatează anumite abilități ale limbajului C (ex. operații pe biți).+
  
-==== Dimensiunea tipurilor implicite ​în C. Calculul memoriei unui program ====+Un **caracter** se declară ​în C de forma: ''​char a=%%'​a'​%%'';​ Pentru inițializarea lui, se observă că am pus un caracter între apostroafe.
  
-În [[https://​ocw.cs.pub.ro/​courses/​programare/​laboratoare/​lab02|laboratorul 2]] au fost prezentate tipurile ​de date implicite din C și dimensiunea acestora.+Un **şir ​de caractere** presupune practic un vector de caractere, terminat prin caracterul ''​\0''​
  
-Pentru a afla ** dimensiunea ​în bytes ** a unei variabile se poate folosi operatorul **sizeof**.+Compilatorul folosește ​în mod implicit această reprezentare,​ astfel încât cea mai simplă declarație este ''​char c[]="​cuvant"'';​ Observăm aici folosirea ghilimelelor în locul apostroafelor. Acecastă instrucțiune va aloca un spațiu de ''​7''​ octeți pe care va reprezenta șirul de caractere '​cuvant'​ (care are ''​6''​ caractere)
  
-<spoiler Exemplu>​ +Dacă dorim să alocăm un spațiu de memorie mai mare (pentru a putea folosi variabila pentru a stoca șiruri de caractere mai lungi), putem folosi o declarație de tipul ''​char ​c[10] = "​cuvant";''​. Astfel am alocat spațiu suficient pentru un șir de ''​9''​ caractere.
-Fie codul din următorul cod. +
-<​code ​c>+
  
-#​include ​<stdio.h+<note important
- +Deși o inițializare ​de tipul ''​char c[6] = "cuvant"''​în care spațiul alocat este egal cu numărul de caracterenu va determina compilatorul să genereze un warning/o eroareacest lucru poate avea rezultate neașteptate dacă în memorie - la finalul șirului - nu se află (întâmplătorvaloarea ​binar. ​ 
-int main() { +</​note>​
-    // afiseaza dimensiunea tipurilor si a unor variabile ​de un anumit tip +
-    ​char xc; +
-    printf("​sizeof(unsigned char) %ld B\n", sizeof(unsigned char)); +
-    printf("​sizeof(char) = %ld B\n", ​sizeof(char));​ +
-    printf("​sizeof(xc) = %ld B\n"sizeof(xc));​ +
- +
-    short int xs; +
-    printf("​sizeof(unsigned short int) = %ld B\n", sizeof(unsigned short int)); +
-    printf("​sizeof(short int) = %ld B\n", sizeof(short int)); +
-    printf("​sizeof(xs) = %ld B\n", sizeof(xs));​ +
- +
-    int xi; +
-    printf("​sizeof(unsigned int) = %ld B\n", sizeof(unsigned int)); +
-    printf("​sizeof(int) = %ld B\n", sizeof(int));​ +
-    printf("​sizeof(xi) = %ld B\n", sizeof(xi));​ +
- +
-    ​// afiseaza dimensiunea unor tablouri cu dimensiune cunoscuta +
-    char vc[100]; +
-    short int vs[100]; +
-    int vi[100]; +
-    printf("​sizeof(vc) = %ld B\n"sizeof(vc)); +
-    printf("​sizeof(vs) = %ld B\n", sizeof(vs));​ +
-    printf("​sizeof(vi) = %ld B\n", sizeof(vi));​ +
- +
-    return ​0; +
-}+
  
 +Cum s-a prezentat anterior, o variabilă vector conţine adresa de început a vectorului(adresa primei componente a vectorului),​ şi de aceea este echivalentă cu un pointer la tipul elementelor din vector. Deci declaraţiile de mai jos vor declara fiecare cate un şir de caractere:
  
 +<code c>
 +char a[5];
 +char *b="​unsir";​
 +char *c;
 </​code>​ </​code>​
  
-În urma executării acestui program ​pe o arhitectură de **32 biți** ​(ceea ce folosim la PCvom vedea următorul rezultat.+Diferenţa majoră dintre între ele este însa că primele două declaraţii vor aloca 5 pozitii în memorie, ​pe când ultima nu va aloca nici zona de memorie, necesitând sa fie ulterior alocată, folosind funcţiile ​de alocare dinamică (''​malloc(), calloc(), realloc()''​),​ prezentate în laboratorul anterior.
  
-<​code>​+Între prima și a 2-a declarație,​ diferența este mai subtilă și constră în faptul că declarația ''​char *b="​unsir"''​ va determina compilatorul să plaseze șirul respectiv într-o zonă de memorie asupra căreia nu avem drepturi de scriere, deci orice încercare de a modifica acel șir - pe Linux - va genera, cel mai probabil, o eroare de tipul ''​segmentation fault''​.
  
-sizeof(unsigned char) = 1 B +Un mic exemplu de citire a unui şir, caracter cu caracter pana la întâlnirea caracterului ''​-'':​
-sizeof(char) = 1 B +
-sizeof(xc) = 1 B+
  
-sizeof(unsigned short int) = 2 B +<code c> 
-sizeof(short int) = 2 B +#include <​stdio.h>​ 
-sizeof(xs) = 2 B +#include <​string.h>​
- +
-sizeof(unsigned int) = 4 B +
-sizeof(int) = 4 B +
-sizeof(xi) = 4 B +
- +
-sizeof(vc) = 100 B +
-sizeof(vs) = 200 B +
-sizeof(vi) = 400 B+
  
 +#define N 30
 + 
 +int main () {
 +   char str[N], c;
 +   int n = 0;
 + 
 +   do {
 +     ​scanf("​%c",​ &c);
 +     if (c == '​-'​) {
 +       ​break;​
 +     }
 +     ​str[n++] = c;
 +   } while(1);
 +   
 +   ​str[n] = '​\0';​ // setam terminatorul de șir
 +   ​printf("​%s",​ str);
 +   
 +   ​return 0;
 +}
 </​code>​ </​code>​
-</​spoiler>​ 
  
-<​note>​ +

Pentru citirea si afisarea ​unui şir de caractere se poate folosi flagul '**s**' la citirea cu ''​scanf''​ sau afişarea cu ''​printf''​. De asemenea biblioteca ''​stdio.h''​ defineşte funcţiile ''​gets()''​ şi ''​puts()''​ pentru lucrul cu şiruri de caractere.
-Putem afla dimensiunea ​unui tip de date / unei variabile de un anumit tip la ** compile time ** folosind operatorul sizeof care returnează dimensiunea în bytes a parametrului dat.+
  
-**sizeof** poate fi folosit ​**și** pentru măsurarea dimesiunii unui vector / matrice alocat(astatic.+  ​''​gets(zona)''​ -– citeşte de la terminalul standard un şir de caractere terminat cu linie noua (enter). 
 +      ​Funcţia are ca parametru adresa zonei de memorie în care se introduc caracterele citite. 
 +      ​Funcţia returneaza adresa de început a zonei de memorie. 
 +  ​''​puts(zona)''​ -- afişeaza la terminalul standard şirul de caractere din zona data ca parametru, până la caracterul terminator de şir (\0), în locul căruia va afișa caracterul sfârșit de linie. 
 +      ​Funcţia are ca parametru adresa zonei de memorie de unde începe afişarea caracterelor. 
 +      ​Funcţia returneaza codul ultimului caracter ​(diferit de \0din şirul de caractere afişat şi -1 dacă a aparut o eroare.
  
-Memoria totală folosită ​de un program poate fi calculată ca **suma** tuturor dimensiunilor ocupate de variabilele din program. +<note important>​ 
- +Funcţia ''​gets()''​ va citi de la tastatura câte caractere sunt introduse, chiar daca şirul declarat are o lungime mai mică. Presupunem un şir declarat: ''​char a[]=”unsir”'' ​care va avea deci 5 caractere. Citind un şir de lungime mai mare ca 5 de la tastaturaîn şirul ala afişare vom vedea ca s-a reţinut tot sirul (nu doar primele 5 caractere). Nimic deosebit până acum. Dar dacă luăm în considerare că citirea caracterelor auxiliare se face în continuare în zona de memoriene punem problema ce se va suprascrie?​! 
-De obiceine interesează să știm ** ordinul ​de mărime ** al spațiului ​de memorie alocatastfel, de cele mai multe oriputem contoriza ​doar tablourile. +Raspunsul ​este: nu se ştie... poate nimic important pentru programul nostru, poate ceva ce il va bloca sau duce la obţinerea ​de date eronate.
- +
- +
-<note warning>​ +
-Un caz special îl poate reprezenta recursivitateaPunerea parametrilor pe stivă de un număr foarte mare de ori, este echivalent cu declararea unui tablou ​de valori pe stivăAceste variabile nu pot fi neglijate în calculul memoriei!+
 </​note>​ </​note>​
  
-Vom descoperi mai multe în următoare laboratoare. 8-) +Pentru a evita aceasta se recomandă utilizarea ''​fgets()''​.
-</​note>​+
  
-==== Operatori pe biți în C ====+  * ''​fgets(zona,​ lung_zona, stdin)''​ -– citeşte de la ''​stdin''​ un şir de caractere terminat printr-o linie nouă dacă lungimea lui este mai mică decat ''​lung_zona''​ sau primele ''​lung_zona - 1''​ caractere în caz contrar. Parametrii sunt: zona de memorie, lungimea maxima admisă a şirului, şterminalul standard de intrare. În cazul în care şirul dorit are lungime mai mică decât cea maximă, înaintea terminatorului de şir (\0), în zona de memorie va fi reţinut şi enter-ul dat(\n).
  
-Operatorii limbajului C pot fi unari, binari sau ternari, fiecare având o precedenţă şi o asociativitate bine definite (vezi [[https://​ocw.cs.pub.ro/​courses/​programare/​laboratoare/​lab02 | lab02]]).+=== Funcţii din <string.h> ===
  
-În tabelul următor reamintim operatorii limbajului C care sunt folosiți la nivel de bit.+Pentru manipularea şirurilor ​de caractere în limbajul C se folosesc funcţii declarate în fişierul <string.h>. Vom încerca să le detaliem putin pe cele mai des folosite:
  
-^ Operator ​ ^  Descriere ​ ^  Asociativitate ​ ^ +== strlen() ​==
-|  **%%~%%** ​ | Complement faţă de 1 pe biţi |  dreapta-stânga ​ | +
-|  **%%<<​%% si %%>>​%%** ​ | Deplasare stânga/​dreapta a biţilor |  stânga-dreapta ​ | +
-|  **&​** ​ | ŞI pe biţi |  stânga-dreapta ​ | +
-|  **^**  | SAU-EXCLUSIV pe biţi |  stânga-dreapta ​ | +
-|  **|**  | SAU pe biţi |  stânga-dreapta ​ | +
-|  **&=** și **|=**  | Atribuire cu ŞI/SAU |  dreapta-stânga ​ | +
-|  **^=**  | Atribuire cu SAU-EXCLUSIV |  dreapta-stânga ​ | +
-|  **%%<<​=%%** şi **%%>>​=%%** ​ | Atribuire cu deplasare de biţi |  dreapta-stânga ​ |+
  
-<note warning+<code c
-Trebuie avută în vedere precedenţa operatorilor pentru obţinerea rezultatelor dorite!+size_t strlen(const char *str); 
 +</​code>​
  
-Dacă nu sunteți sigur de precendența ​unui operator, folosiți o pereche ​de paranteze rotunde în plus în expresia voastră! Nu exagerați cu parantezele,​ codul poate deveni ilizibil. +Returneaza lungimea ​unui şir dat ca parametru. (numarul ​de caractere până la întalnirea terminatorului de şir: \0)
-</​note>​+
  
-== Bitwise NOT == +**Exemplu:**
-<spoiler Semnificație>​ +
-** Bitwise NOT ** (**complement față de 1**) este operația la nivel de bit care următorul tabel de adevăr. +
-^ x ^ ~x ^ +
-^ 0 ^ 1 ^ +
-^ 1 ^ 0 ^+
  
-Evident putem extinde această operație și la nivel de număr. Operația se aplică separat pentru fiecare rang binar+<code c> 
-^  ^ $b_2$ ^  $b_1$ ^ $b_0$ ^ +#include <stdio.h> 
-^  x = 3 ^ 0 ^ 1 ^ 1 ^ +#​include ​<string.h>
-^ ~x = 4 ^ 1 ^ 0 ^ 0 ^ +
-</spoiler>+
  
-== Bitwise AND == +#define N 256 
-<spoiler Semnificație>​ +  
-** Bitwise AND ** (**ȘI pe biți**este operația la nivel de bit care următorul tabel de adevăr. +int main () { 
-^ x ^ y ^ x & y ^ +  char text[N]; 
-^ 0 ^ 0 ^ 0 ^ +  ​printf("​Introduceti un text: "); 
-^ 0 ^ 1 ^ 0 ^ +  ​gets(text);​ 
-^ 1 ^ ^ 0 ^ +  printf("​Textul are %u caractere.\n",​ strlen(text));​ 
-^ 1 ^ 1 ^ 1 ^+  ​return ​0; 
 +
 +</​code>​
  
-Evident putem extinde această operație și la nivel de număr. Operația se aplică separat pentru fiecare rang binar. +**Iesire:**
-^  ^ $b_2$ ^  $b_1$ ^ $b_0$ ^ +
-^     x = 3 ^ 0 ^ 1 ^ 1 ^ +
-^     y = 7 ^ 1 ^ 1 ^ 1 ^ +
-^ x & y = 3 ^ 0 ^ 1 ^ 1 ^ +
-</​spoiler>​+
  
-== Bitwise OR == +<code
-<spoiler Semnificație+Introduceti un text: just testing 
-** Bitwise OR ** (**SAU pe biți**) este operația la nivel de bit care următorul tabel de adevăr+Textul are 12 caractere
-^ x ^ y ^ x %%|%% y ^ +</​code>​
-^ 0 ^ 0 ^ 0 ^ +
-^ 0 ^ 1 ^ 1 ^ +
-^ 1 ^ 0 ^ 1 ^ +
-^ 1 ^ 1 ^ 1 ^+
  
-Evident putem extinde această operație și la nivel de număr. Operația se aplică separat pentru fiecare rang binar. +== memset() ==
-^   ^ $b_2$  ^  $b_1$ ^ $b_0$ ^ +
-^ x ^ 0 ^ 1 ^ 1 ^ +
-^ y ^ 1 ^ 0 ^ 1 ^ +
-^ x %%|%% y 7 ^ 1 ^ 1 ^ 1 ^ +
-</​spoiler>​+
  
-== Bitwise XOR == +<code c
-<spoiler Semnificație+voidmemset(void *ptr, int val, size_t num); 
-** Bitwise XOR ** (**SAU-EXCLUSIV pe biți**este operația la nivel de bit care următorul tabel de adevăr. +</​code>​
-^ x ^ y ^ x %%^%% y ^ +
-^ 0 ^ 0 ^ 0 ^ +
-^ 0 ^ 1 ^ 1 ^ +
-^ 1 ^ 0 ^ 1 ^ +
-^ 1 ^ 1 ^ 0 ^+
  
-Evident putem extinde această operație și la nivel de numărOperația se aplică separat pentru fiecare rang binar. +În zona de memorie dată de pointerul ptr, sunt setate primii num octeți la valoarea dată de val. Pentru șiruri de caractere - în care fiecare element ocupă 1 octet - aceasta are ca rezultat înlocuirea primelor 'num' valori cu cea dată ca argument
-^       ^ $b_2$ ^  $b_1$ ^ $b_0$ ^ +
-^ x = 3 ^ 0 ^ 1 ^ 1 ^ +
-^ y = 5 ^ 1 ^ 0 ^ 1 ^ +
-^ x %%^%% y = 6 ^ 1 ^ 1 ^ 0 ^ +
-</​spoiler>​+
  
-== Bit LOGICAL SHIFT == +Funcţia returnează şirul ptr.
-În C sunt definite doar shiftări logice. Acestea pot fi la stânga (%%<<​%%) sau la dreapta (%%>>​%%),​ reprezentând deplasarea in binar a cifrelor și completarea pozițiilor "​golite"​ cu zerouri.+
  
-== LEFT SHIFT == +**Exemplu:​**
-<​note>​  +
-Efectul unei deplasări la stânga cu un rang binar este echivalent cu înmulțirea cu 2 a numărului din baza 10. +
-Dacă rezultatul nu are loc pe tipul de date folosit, atunci se pot pierde din biți!+
  
-Se poate deduce următoarea relație: $ n << ​k = n * 2^k $+<code c> 
-</note>+#​include ​<stdio.h> 
 +#include <​string.h>​ 
 +  
 +int main () { 
 +  char str[] = "nu prea vreau vacanta!";​ 
 +  memset(str, '​-',​ 7); 
 +  puts(str);​ 
 +  return 0; 
 +} 
 +</code>
  
-<spoiler Exemplu>​ +**Iesire:**
-Fie un exemplu de deplasarea la stânga, pentru un număr ​** pe 3 biți **.  +
-^            ^ $b_2$ ^  $b_1$ ^ $b_0$ ^ +
-^ x = 3      ^ 0 ^ 1 ^ 1 ^ +
-^ x << 1 = 6 ^ 1 ^ 1 ^ 0 ^ +
-^ x << 2 = 8 ^ 1 ^ 0 ^ 0 ^ +
-</​spoiler>​+
  
-== RIGHT SHIFT == +<code
-<note>  +------- vreau vacanta! 
-Efectul unei deplasări la dreapta cu un rang binar este echivalent cu împărțirea întreagă la 2 a numărului din baza 10.+</​code>​
  
-Se poate deduce următoarea relație: $ n >> k = [n / 2^k ] $. +<​note ​important> 
-</note>+Ca programatori,​ veți întâlni adesea situații în care ''​memset''​ este folosit pentru inițializarea de vectori de diverse tipuri.
  
-<spoiler Exemplu>​ +Atunci când scrieți sau evaluați cod care face acest lucru trebuie să aveți în vedere că memset face scrierea valorii primite pe fiecare ​**octet** (fără a ține cont de dimensiunea reperezentării tipului de date)Următorul cod arată cum putem folosi memset pentru a inițializa un vector de int la folosind memset, respectiv care ar fi rezultatul dacă am încerca să inițializăm vectorul cu o altă valoare:
-Fie un exemplu de deplasarea la dreapta, pentru un număr ** pe 3 biți **.  +
-^  ^ $b_2$ ^  $b_1$ ^ $b_0$ ^ +
-^ x = 3 ^ 0 ^ 1 ^ 1 ^ +
-^ x >> 1 = 1 ^ ^ 0 ^ 1 ^ +
-^ x >> 2 = 0 ^ 0 ^ 0 ^ 0 ^ +
-</​spoiler>​+
  
 +<code c>
 +#include <​stdio.h>​
 +#include <​string.h>​
  
 +#define SIZE 2
  
 +int main(void)
 +{
 +    int a[SIZE], b[SIZE], i;
 +    memset(a, 0, SIZE * sizeof(int));​
 +    memset(b, 5, SIZE * sizeof(int));​
  
-==== Lucrul cu măști ==== +    for(i 0; i < SIZE; i++) 
-Având la dispoziție operațiile prezentate mai susputem răspunde la următoarele întrebări.+        ​printf("​%d "a[i]);
  
-  * Cum verificăm dacă ** bitul i ** dintr-un număr ​este ** setat **? +    printf("​\n");
-  * Cum ** setăm bitul i ** dintr-un număr n? +
-  * Cum ** resetăm bitul i ** dintr-un număr n? +
-   +
-Pentru a răspunde ușor, pentru fiecare întrebare vom aplica o operație pe biți între n și o valoarea numită ** mască **.+
  
 +    for(i = 0; i < SIZE; i++)
 +        printf("​%d ", b[i]);
  
 +    return 0;
 +}
 +</​code>​
 +Rezultatul este...
 +<​code>​
 +0 0 
 +84215045 84215045
 +</​code>​
 +rezultatul neașteptat de pe a 2-a linie provenind din faptul că fiecare octet al int-urilor a fost setat la 5, și nu valoarea întregii structuri.
  
 +Este de menționat faptul că utilizarea ''​memset''​ în astfel de situații **nu** este recomandată.
  
-=== Cum verificăm dacă  bitul i  dintr-un număr n este setat? === 
-<spoiler Explicație>​ 
- 
-Această întrebare ne oferă valoarea bitului i. 
- 
-Dacă "​valoarea este 1", atunci vom spune că "bitul este setat"​. ​ 
- 
-Dacă "​valoarea este 0", atunci vom spune că "bitul nu este "​setat"​. 
- 
- 
-Pentru a ** verifica ** valoarea bitului i din numărul n, practic noi ar trebui să privim numărul astfel: 
- 
-^    ^ $b_7$ ^ $b_6$ ^ ... ^ $b_i$ ^ ... ^ $b_1$ ^ $b_0$ ^ 
-^ n  ^ *  ^  * ^ ... ^  ? ^ ... ^ *  ^  * ^ 
- 
-unde ***** înseamnă ** don't care ** (de la PL), 
-iar **?** este valoarea pe care o cautăm. 
- 
-Deci am vrea sa facem, după cum am zis mai sus, o operație de tipul "​scoate"​ doar bitul i din număr, iar în rest lasă 0 (pentru a evidenția bitul nostru). 
-^            ^ $b_7$ ^ ... ^ $b_{i+1}$ ​ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ 
-^         ​n ​ ^ *     ^ ... ^  *         ​^ ​ ?    ^  *        ^ ... ^  *    ^    ^ 
-^      mask  ^ $m_7$ ^ ... ^ $m_{i+1}$ ​ ^ $m_i$ ^ $m_{i-1}$ ^ ... ^ $m_0$ ^ op ^ 
-^ n op mask  ^ 0     ^ ... ^  0         ​^ ​ ?    ^  0        ^ ... ^  0    ^    ^ 
- 
-Să analizăm cine pot fi **op** și **biții din mască** ($m_i$). 
- 
-Dorim ca: 
-  * $ ? \ \ op \ \ m_i = \ ? $ : operația op aplicată pe $?$ și $m_i$, va avea mereu ca rezultat pe $?$. 
-  * $ * \ \ op \ \ m_j = 0 $ (unde i != j) : operația op aplicată pe orice valoare și $m_j$, va da 0. 
- 
-Observăm că: 
-  * 1 este elementul neutru pentru **ȘI**, ceea ce verifică ** ? & 1 = ? **, oricare are fi ? un bit. 
-  * 0 este elementul care poate "​șterge"​ un bit prin **Și**, ceea ce verifică ** * & 0 = 0 **, oricare ar fi * un bit. 
- 
-</​spoiler>​ 
-<​note>​ 
-Detectarea bitului: 
- 
-  * pas 1: se aplică următoarea operația ** x = n & mask **, unde ** mask = (1 << i) ** 
- 
-^       ^ $b_7$ ^ ... ^ $b_{i+1}$ ​ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ 
-^    n  ^ *     ^ ... ^  *         ​^ ​ ?    ^  *        ^ ... ^  *    ^    ^ 
-^ mask  ^ 0     ^ ... ^  0         ​^ ​ 1    ^  0        ^ ... ^  0    ^ op ^ 
-^    x  ^ 0     ^ ... ^  0         ​^ ​ ?    ^  0        ^ ... ^  0    ^    ^ 
- 
- 
-  * pas 2: deoarece **?** poate avea două valori, x poate fi $0$ sau $2^i$. 
-         * dacă ** x == 0 **, atunci ** bitul i este 0 ** 
-         * dacă ** x > 0  **, atunci ** bitul i este 1 **  
 </​note>​ </​note>​
  
 +== memmove() ==
  
-<spoiler Implementare C+<code c> 
-<​code ​C>+void* memmove(void *destination,​ const void *source, size_t num); 
 +</code>
  
 +Copiază un număr de num caractere de la sursă, la zona de memorie indicată de destinaţie. Copierea are loc ca şi cum ar exista un buffer intermediar,​ deci sursa si destinatia se pot suprapune. Funcţia nu verifică terminatorul de şir la sursă, copiază mereu num bytes, deci pentru a evita depăsirea trebuie ca dimensiunea sursei sa fie mai mare ca num. Funcţia returnează destinaţia.
  
-// is_set +**Exemplu:​**
-// byte    - byte de intrare pentru care vreau sa verific un bit +
-// i       - indexul bitului din byte +
-// @return - 1, daca bitul este 1 +
-//           0, daca bitul este 0 +
-int is_set(char byte, int i) { +
-    int mask = (1 << i); +
-    return (byte & mask) != 0; +
-}+
  
-... +<code c> 
-if (is_set(mybyte,​ i)) { +#include <stdio.h> 
-    ​printf("bitul %d din byteul %d este setat!\n", ​imybyte); +#include <string.h> 
-} else { +  
-    printf("bitul %d din byteul %d NU este setat!\n",​ i, mybyte);+int main () { 
 +  char str[] = "memmove can be very useful......"
 +  memmove(str + 20str + 1511); 
 +  puts(str)
 +  return 0;
 } }
-... 
 </​code>​ </​code>​
-</​spoiler>​ 
  
 +**Iesire:**
  
 +<​code>​
 +memmove can be very very useful.
 +</​code>​
  
 +== memcpy() ==
  
-=== Cum setăm (valoarea devine 1)  bitul i  dintr-un număr n? === +<code c
-<spoiler Explicație+void* memcpy(void *destinationconst void *source, size_t num); 
-Dorim să facem următoarea operație: schimba doar bitul i in 1iar pe ceilalți lasă-i neschimbați. +</​code>​
-^            ^ $b_7$ ^ ... ^ $b_{i+1}$ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ +
-^         ​n ​ ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  ​   ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^ +
-^      mask  ^ $m_7$ ^ ... ^ $m_{i+1}$ ^ $m_i$ ^ $m_{i-1}$ ^ ... ^ $m_0$ ^ op ^ +
-^ n op mask  ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  1    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^+
  
-Să analizăm cine pot fi **op** și **biții ​din mască** ($m_i$).+Copiază un număr de num caractere ​din şirul sursă in şirul destinaţie. Funcţia returnează şirul destinaţie.
  
-Dorim ca: +**Exemplu:**
-  ​\ \ op \ \ m_i = 1 $ operația op aplicată pe $*$ (orice) și $m_i$, va avea mereu ca rezultat pe $1$. +
-  ​$ n_j \ \ op \ \ m_j = n_j $ (unde i != j) : operația $op$ aplicată pe orice valoare și $m_j$, va da $n_j$.+
  
-Observăm ​că: +<​code ​c> 
-  * 1 esteelementul care poate "​umple"​ un bit prin **SAU**, ceea ce verifică $ * | 1 = 1 $, oricare ar fi * un bit. +#include <stdio.h> 
-  * 0 este elementul neutru pentru **SAU**, ceea ce verifică $ n_j | 0 = n_j $, oricare are fi $n_j$ un bit+#​include ​<string.h>
-</spoiler>+
  
-<​note>​ +#define N 40
-Setarea bitului: +
-  * pas 1: se aplică următoarea operația ** n = n | mask **, unde ** mask = (1 << i) ** +
- +
-^            ^ $b_7$ ^ ... ^ $b_{i+1}$ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ +
-^         ​n ​ ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  *    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^ +
-^      mask  ^ $0  $ ^ ... ^ $0      $ ^ $1  $ ^ $0      $ ^ ... ^ $0  $ ^ op ^ +
-^ n op mask  ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  1    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^+
  
- +int main () { 
-  ​* explicație:​ +  ​char str1[] = "​Exemplu";​ 
-         * orice valoare are avea bitul $*$ va fi suprascris cu 1 +  char str2[N]; 
-         * ceilalti biți vor fi copiați +  char str3[N]; 
- +  ​memcpy(str2,​ str1, strlen(str1) + 1); // + 1 este necesar ​pentru ​a copia șterminatorul de șir 
-</note> +  ​memcpy(str3"un sir", 7); 
- +  ​printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3); 
-<spoiler Implementare C> +  return ​0;
-<code C> +
- +
-// set +
-// byte    - byte de intrare ​pentru ​care vreau sa setez un bit +
-//       - indexul bitului din byte +
-// @return - noul byte +
-char set(char byteint i{ +
-    int mask = (1 << i); +
-    return ​(byte | mask);+
 } }
- 
-... 
-mybyte = set(mybyte, i); 
-... 
-    ​ 
 </​code>​ </​code>​
-</​spoiler>​ 
  
 +**Iesire:**
  
 +<​code>​
 +str1: Exemplu
 +str2: Exemplu
 +str3: un sir
 +</​code>​
  
 +== strcpy() ==
  
 +<code c>
 +char* strcpy(char *destination,​ const char *source);
 +</​code>​
  
-=== Cum resetăm (valoarea devine 0)  bitul i  dintr-un număr n? === +Copiază şirul sursă in şirul destinaţie. Şirul destinaţie va fi suprascris. Funcţia asigură plasarea terminatorului de şir în şirul destinaţie după copiereFuncţia returneaza şirul destinaţie.
-<spoiler Explicație+
-Dorim să facem următoarea operație: schimba doar bitul i in 0, iar pe ceilalți lasă-i neschimbați. +
-^            ^ $b_7$ ^ ... ^ $b_{i+1}$ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ +
-^         ​n ​ ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  *    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^ +
-^      mask  ^ $m_7$ ^ ... ^ $m_{i+1}$ ^ $m_i$ ^ $m_{i-1}$ ^ ... ^ $m_0$ ^ op ^ +
-^ n op mask  ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  0    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^+
  
-Să analizăm cine pot fi **op** și **biții din mască** ​($m_i$).+== strncpy() ==
  
-Dorim ca: +<code c> 
-  \ \ op \ \ m_i = 0 $ : operația op aplicată pe $*$ (oriceși $m_i$, va avea mereu ca rezultat pe $0$. +charstrncpy(char ​*destination,​ const char *source, size_t num); 
-  * $ n_j \ \ op \ \ m_j = n_j $ (unde i != j) : operația $op$ aplicată pe orice valoare și $m_j$, va da $n_j$.+</​code>​
  
-Observăm că: +Asemeni cu ''​strcpy()''​dar in loc de a fi copiată toata sursa sunt copiate doar primele //num// caractere.
-  * 0 este elementul care poate "​șterge"​ un bit prin **ȘI**ceea ce verifică $ * & 0 = 0 $, oricare ar fi * un bit. +
-  * 1 este elementul neutru pentru **ȘI**, ceea ce verifică $ n_j | 1 = n_j $, oricare are fi $n_j$ un bit. +
-</​spoiler>​+
  
-<​note>​ +**Exemplu:**
-Resetarea bitului:+
  
-  * pas 1: se aplică următoarea operația ** n = n & mask **, unde ** mask = ~(1 << ​i) **+<code c> 
 +#​include ​<stdio.h>​ 
 +#include <​string.h>​
  
-^            ^ $b_7$ ^ ... ^ $b_{i+1}$ ^ $b_i$ ^ $b_{i-1}$ ^ ... ^ $b_0$ ^    ^ +#define N 40
-^         ​n ​ ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  *    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^ +
-^      mask  ^ $1  $ ^ ... ^ $1      $ ^ $0  $ ^ $1      $ ^ ... ^ $1  $ ^ op ^ +
-^ n op mask  ^ $n_7$ ^ ... ^ $n_{i+1}$ ^  0    ^ $n_{i-1}$ ^ ... ^ $n_0$ ^    ^+
  
- +int main () { 
-  ​* explicație:​ +  ​char str1[] = "​Exemplu";​ 
-         * orice valoare are avea bitul $*$ va fi suprascris cu 0 +  char str2[N]; 
-         * ceilalti biți vor fi copiați +  char str3[N]; 
- +  ​strcpy(str2,​ str1); 
-</​note>​ +  ​strncpy(str3"un sir", 2); 
- +  ​str3[2] ​'​\0';​ 
- +  printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3); 
-<spoiler Implementare C> +  return ​0;
-<code C> +
- +
-// reset +
-// byte    - byte de intrare pentru care vreau sa resetez un bit +
-// i       - indexul bitului din byte +
-// @return - noul byte +
-char reset(char byteint i{ +
-    int mask ~(1 << i); +
-    return ​(byte & mask);+
 } }
- 
-... 
-mybyte = reset(mybyte,​ i); 
-... 
-    ​ 
 </​code>​ </​code>​
-</​spoiler>​ 
- 
- 
-==== Exerciții ​ ==== 
-[[https://​drive.google.com/​drive/​u/​1/​folders/​1qB6EZLGVubKbuTXMtMue06egH_8fo25M|Checker laborator 7 CB\CD]] 
-[[https://​drive.google.com/​open?​id=1oBRfwcYfuosDaBRkeq7f_XmQYayOqbZq | Teste Problema 1]] 
-[[https://​drive.google.com/​open?​id=16kc4UllXMXb62KYIyZ8AndclaRjBTQ0Q | Teste Problema 2]] 
-[[https://​drive.google.com/​open?​id=1kO2oT9cF9Oih0Jav00Ji9gHuhSnsMo5R | Teste Problema 3]] 
-[[https://​drive.google.com/​open?​id=1MVOlZdZv7aZ3DAdMRn2QQL2DkjkCb6Xh | Teste Problema 4]] 
-[[https://​drive.google.com/​open?​id=1TsXI7HkbLOR2kwM0lLe-1z3qcSs3eT0z | Teste Problema 5]] 
-[[https://​drive.google.com/​open?​id=1QjYcHcDSApXZ1ZTloxqibOAU9GSckaxE | Teste Problema 6]] 
- 
-==Precizari:​== 
- 
- 
-    * Arhivele 4, 5, 6 testeaza reuniunea, intersectia respectiv diferenta seturilor. 
-    * Intrarea corespunde functiei set_read (n, urmat de n elemente) 
-    * Ref corespunde functiei set_print aplicata pe setul obtinut (cardinalul setului, urmat pe urmatoarea linie de elementele din set) 
- 
-    * Arhiva 7 corespunde problemei de bonus B1 (.ref contine rezultatul pentru get_lsb urmat de rezultatul pentru get_msb) 
-    * Arhiva 8 corespunde problemei B2. 
  
 +**Iesire:**
  
- +<code
- +str1: Exemplu 
-<note warning+str2Exemplu 
-În rezolvarea acestui ** laborator ** nu aveți voie să folosiți operatorii ** /, *, % ** ! +str3: un
- +
-HintÎncercați să folosiți operații pe biți! ​ +
-</​note>​ +
- +
-Rezolvați împreună cu asistentul, eventual pe tablă, exercițiile 0-3, apoi rezolvați invidual exercițiul 4. +
- +
-== 0Verificare ca un număr e par == +
-Să se verifice folosind operații pe biți că un număr natural n e par. +
-<code C> +
-int is_even(int n); +
-</​code>​ +
-== 1. Calcul putere a lui 2 (0p) == +
-Să se scrie o funcție care să calculeze $2^n$, unde $ n <= 30 $. +
-<code C> +
-int pow2(int n);+
 </​code>​ </​code>​
-Răspundeți la întrebarea:​ **are sens** să scriem o funcție? 
  
-== 2. Negarea biților unui număr n (0p) == +== strcat() ==
-Să se scrie o funcție care să nege biții unui număr n (32 biți). +
-<code C> +
-int flip_bits(int n); +
-</​code>​ +
-Răspundeți la întrebarea:​ **are sens** să scriem o funcție?+
  
-== 3. Afișarea biților unui număr n (0p)== +<​code ​c
-Să se scrie o funcție care să afișeze toți biții unui număr întreg pe 32 biți. +char* strcat(char *destination,​ const char *source);
-<​code ​C+
-void print_bits(int n);+
 </​code>​ </​code>​
  
-Implementați invidual următoarea problemă. +Concatenenaza şirul sursă la şirul destinaţieFuncţia returnează şirul destinaţie.
-== 4. bitset (10p) == +
-O mulțime de numere întregi poate fi reprezentată astfel: spunem că un număr aparține unei mulțimi S dacă bit-ul al i-lea din vectorul S are valoarea 1+
  
-Pentru e ficientă,​ vectorul S va conține date de tipul **unsigned char**. +<note critical
- +Șirul destinație trebuie să aibă suficientă memorie alocată pentru a a acomoda șirul rezultat.
-<spoiler Exemplu+
-Presupunem că avem vectorul ** unsigned char S[3]; **, pentru care am initializat deja valorile cu 0, 3, 5. +
-S[0], S[1], S[2] fiecare reprezintă câte o variabilă pe 8 biți. +
- +
-^  ^     ​^ ​    ​^ ​    ​^ ​  ​S[0] ​ ^     ​^ ​    ​^ ​    ​^ ​    ​^ ​ |   ​^ ​    ​^ ​    ​^ ​    ^ S[1]    ^     ​^ ​    ​^ ​    ​^ ​    ​^ ​  ​| ​ ^     ​^ ​    ​^ ​    ^ S[2]    ^     ​^ ​    ​^ ​    ​^ ​    ^ +
-^ ^$b_0$^$b_ 1$^$b_2$^$b_3$^$b_4$^$b_5$^$b_6$^$b_7$^ ​ |   ^ $b_0$^$b_1$^$b_2$^$b_3$^$b_4$^$b_5$^$b_6$^$b_7$^ ​ |   ^ $b_0$^$b_1$^$b_2$^$b_3$^$b_4$^$b_5$^$b_6$^$b_7$^ +
-^ biti din S ^0    ^0    ^0    ^0    ^0    ^0    ^0    ^0    ^  |  ^1    ^1    ^0    ^0    ^0    ^0    ^0    ^0    ^    |   ^ 1    ^0    ^1    ^0    ^0    ^0    ^0    ^0    ^ +
-^ numere asociate^0^1^2^3^4^5^6^7^ | ^8^9^10^11^12^13^14^15^ | ^16^17^18^19^20^21^22^23^  +
- +
-<​note>​ +
-Atentie! Bitii au fost desenati invers (stanga-dreapta 0-7) pentru a obtine in desen sirul sortat de numere asociate. +
-In realitate un byte arata asa in memorie: $b_7b_6b_5b_4b_3b_2b_1b_0$+
 </​note>​ </​note>​
-Observați notarea biților! Vom folosi această schema pentru a obține șirul numerelor asociate ca fiind sortat. 
  
-Interpretare:​+== strncat() ==
  
-- numărul ​**3** nu face partea din mulțimea Spentru că bitul asociat (**cel cu numărul etichete 3** din vector) este **0**+<code c> 
 +charstrncat(char ​*destination,​ const char *sourcesize_t ​num); 
 +</code>
  
-- numărul **16** face partea din mulțimea S, pentru că bitul asociat (**cel cu numărul etichepa 16 ** din vectoreste **1**+Asemeni cu ''​strcat()'',​ dar în loc de a fi concatenată toată sursa sunt concatenate ​**cel mult** primele //num// caractere ​din șirul sursa (aceasta putând fii și mai scurt).
  
-Analog pentru toate numerele de la 0 la 23 (avem doar 24 de biți în acest exemplu), putem verifica dacă numărul i este în vector. +**Exemplu:**
-Astfel vectorul S de mai sus reprezintă mulțimea ​**{8, 9, 16, 18}**.+
  
-Dacă programul nostru va seta bitul cu numărul 15 pe 1, atunci operația este echivalentă cu **S = S U {15}**. Programul va trebui să își dea seama ca **bitul cu numărul 15** este defapt ** bitul cu numărul 7 ** din **byteul cu numărul 1** (adica din S[1]).  +<code c> 
-</spoiler>+#include <stdio.h> 
 +#​include ​<string.h>
  
-<​note>​ +#define N 80
-Pentru a folosi cu ușurință același cod facând schimbări minime (de exemplu schimbăm dimensiunea maximă a unei mulțimi), putem să ne definim propriul tip astfel:+
  
-<code C> +int main () { 
-#define SET_SIZE 100                   // aceasta este o macrodefiniție ​(momentan o putem privi ca pe O CONSTANTA CARE ARE VALOAREA 100 +  char str[N]; 
-typedef unsigned ​char SET[SET_SIZE];   // definesc tipul SETcare este un vector cu maxim 100 de elemente de tip unsigned char+  strcpy(str"ana "); 
 +  strcat(str, "are "); 
 +  strcat(str, "mere "); 
 +  puts(str);​ 
 +  strncat(str,​ "si pere si prune",​ 7); 
 +  puts(str);​ 
 +  return 0; 
 +}
 </​code>​ </​code>​
  
-Cele două linii de mai sus vor fi puse imediat dupa includerea directivelor header!+**Iesire:**
  
-</note>+<​code>​ 
 +ana are mere 
 +ana are mere si pere 
 +</code>
  
-    +== strcmp() ==
-Implementați funcții pentru: +
-      * **adăugarea** unui element în mulțime ​(**1p**) +
-      <code C>  +
-      // insert_in_set(s,​ n) - adauga numarul n in multimea S +
-      void insert_in_set(SET s, unsigned int n); +
-      </​code>​+
  
-      * **ștergerea** unui element din mulțime (**1p**) +<​code ​c
-      ​<​code ​C+int strcmp(const char *str1const char *str2); 
-      // delete_from_set(sn) - scoate numarul n din multime S +</​code>​
-      void delete_from_set(SET s, unsigned int n); +
-      </​code>​+
  
-      * a verifica dacă un element n **aparține** unei mulțimi (**1p**) +Compară şirul //str1// cu şirul //str2//, verificându-le caracter cu caracter. Valoarea returnată ​este 0 daca cele şiruri sunt identicemai mare ca daca str1 este "mai mare"(alfabeticşi mai mic ca 0 altfel.
-      <code C> +
-      ​// is_in_set(sn) returneaza 1 daca n este in S, 0 altfel +
-      int is_in_set(SET s, unsigned int n)+
-      </​code>​+
  
-      ​* **ștergerea** tuturor elementelor din mulțime (**1p**) +**Exemplu:**
-      <code C> +
-      // delete_all_from_set(s) - elimina toate elementele din multime +
-      void delete_all_from_set(SET s); +
-      </​code>​+
  
-      * a calcula **cardinalul** unei mulțimi (**1p**) +<​code ​c
-      ​<​code ​C+#include <​stdio.h>​ 
-      // card_set(s) - returneaza cardinalul multimii S +#​include ​<string.h> 
-      int card_set(SET s); +  
-      </code>+#define N 80
  
-      * a verifica dacă mulțimea este **vidă** ​(**0.5p**+int main () { 
-      <​code C> +  char cuv[] = "​rosu";​ 
-      // is_empty_set(s- verifica daca multimea este sau nu goala +  char cuv_citit[N];​ 
-      // returneaza 1 daca este, 0 daca nu este +  do { 
-      int is_empty_set(SET s); +    printf ​("​Ghiceste culoarea..."​); 
-      </​code>​+    ​gets(cuv_citit);​ 
 +  } while (strcmp(cuv,cuv_citit) != 0); 
 +   
 +  puts("​OK"​); 
 +   
 +  return 0; 
 +
 +</​code>​
  
-      * o funcție care **să citească de la tastatură** ​ o mulțime (**0.5p**) +În situația în care șirurile au lungimi diferite, ultima comparație se face între \și caracterul ​de pe aceași poziție din șirul mai lung, 
-      <code C> +
-      // read_set(a) - functia citeste numarul n de elemente care se afla in A +
-      // apoi citeste cele n numere si le insereaza in A +
-      // va returna numarul n citit (numarul de elemente) +
-      int read_set(SET a); +
-      </​code>​+
  
-      * o funcție care **să afișeze** pe ecran elementele care se află într-o mulțime ​(**0.5p**) +== strchr() ==
-      <code C> +
-      // print_set(a) - functia printeaza elementele multimii A +
-      void print_set(SET a); +
-      </​code>​+
  
-Realizati un program care, utilizând metodele de fite anterior, citește 2 mulțimi A (n și B și a fișează:​ $ A U B, A ∩ B, A - BB - A $.+<code c> 
 +char* strchr(const char *strint character);​ 
 +</​code>​
  
-Pentru a realiza acest lucru, va trebui să implementațurmătoarele funcții: +Caută caracterul //​character//​ în şirul //str// şreturnează un pointer la //prima// sa apariţie sau ''​NULL''​ dacă acesta nu a fost găsit..
-      * **reuniunea** a două mulțimi (**1p**) +
-      <code C> +
-      ​// C = A U B +
-      void merge_set(SET a, SET b, SET c); +
-      </code>+
  
-      * **intersecția** a două mulțimi (**1p**) +== strrchr()==
-      <code C> +
-      // C A n B +
-      void intersect_set(SET a, SET b, SET c)+
-      </​code>​+
  
-      * **diferența** a două mulțimi (**1p**) +<​code ​c
-      ​<​code ​C+char* strrchr(const char *strint character); 
-      // C = A - B +</​code>​
-      void diff_set(SET aSET b, SET c); +
-      </​code>​+
  
-În final va trebui sa creați o funcție ** main ** și să faceți un program care rezolvă cerința folosind funcțiile implementate(**1p**)+Caută caracterul //​character//​ în şirul //str// şreturnează ​un pointer la //ultima// sa apariţie sau ''​NULL''​ dacă acesta nu există în șir.
  
-<spoiler Hint> +== strstr() ==
-Folosiți funcțiile **set, reset, is_set ** definite în ** prima ** parte a laboratorului.+
  
-Implementați funcțiile din enunț în ordinea sugerată. +<code c
-</spoiler+char* strstr(const char *str1const char *str2);
- +
-=== BONUS === +
-== B1. MSB/LSB == +
-Să se scrie o câte o funcție pentru aflarea MSB(Most significant bit)respectiv LSB(Least significant bit), pentru un număr n pe 32 biți. +
-<code C> +
-int get_lsb(int n); +
-int get_msb(int n);+
 </​code>​ </​code>​
-<spoiler Hint> 
-Analizați reprezentarea în baza 2 a lui n. 
-</​spoiler>​ 
  
-== B2. Verificare că un număr este putere al lui 2 == +Caută şirul //str2// în şirul //str1// şi //returnează un pointer la prima sa apariţie//, sau //''​NULL''// ​dacă nu fost găsit.
-Să se scrie o funcție care verifică ​dacă un număr întreg n pe 32 biți este puterea ​lui 2. Funcția va returna 1 dacă n este putere a lui 2, 0 altfel. +
-<code C> +
-int is_power2(int n); +
-</​code>​ +
-<spoiler Hint> +
-Analizați reprezentarea în baza 2 a lui n (ex. n = 16 si n = 5). +
-</​spoiler>​+
  
 +== strdup() ==
  
 +<code c>
 +char* strdup(const char *str);
 +</​code>​
  
 +Realizează un duplicat al şirului //str//, pe care îl şi returnează. Spațiul de memorie necesar copiei este alocată dinamic, fiind responsabilitatea noastră să o dealocăm (așa cum am s-a prezentat laboratorul anterior).
  
-==== Probleme de interviu ==== +**Exemplu:​**
-Pentru cei interesați,​ recomandăm rezolvarea și următoarelor probleme, care sunt des întâlnite la interviuri. +
-<note warning>​ +
-Atenție! Problemele din această categorie au un nivel de dificultate ridicat, peste cel cerut la cursul de PC.+
  
-Recomandăm totuși rezolvarea acestor probleme pentru cei care doresc să aprofundeze lucrul cu operații pe biți+<code c> 
-</note>+#include <stdio.h> 
 +#​include ​<string.h>
  
-<spoiler Swap bits> +#define N 80 
-Se dă un număr n natural pe 32 de biți. Se cere să se calculeze numărul obținut prin interschimbarea biților de rang par cu cel de rang impar. +  
- +int main () { 
-Exemplu: n 2863311530 ​> m 1431655765+  char str[N] = "​salut",​ *d; 
 +  
 +  ​d ​strdup(str);​ 
 +  if(d == NULL) { 
 +      printf("​Eroare!\n"​);​ 
 +      return -1; 
 +  } 
 +   
 +  puts(d); 
 +  free(d); 
 +   
 +  return 0; 
 +
 +</​code>​
  
 <​note>​ <​note>​
-Hint: Reprezentarea numerelor în baza 2 ([http://www.binaryhexconverter.com/​decimal-to-binary-converter | convertor]]).+''​strdup(..)''​ va aloca întotdeuna ''​strlen() + 1''​ octeți pentru destinație,​ indiferent de dimensiunea memoriei alocate pentru sursă.
 </​note>​ </​note>​
  
-</​spoiler>​+== strtok() ==
  
-<spoiler Element unic din șir>  +<code c
-Fie un șir cu ** 2n + 1 ** numere întregi, dintre care n numere apar de câte 2 ore, iar unul singur este unic. +charstrtok(char ​*str, const char *delimitators);​ 
-Să se gasească elementul unic. +</​code>​
  
-Exemplu: +Funcţia are rolul de a împarţşirul //str// în tokens(subşiruri separate de orice caracter aflat în lista de delimitatori)prin apelarea ei succesivă.
-        n = 5 șsirul [14, 4, 1, 5] +
-        Numărul unic este 5 +
-<​note> ​        +
-Hint: Încercați să nu folosiți tablouri. +
-</​note>​+
  
-Follow-up 1: Șirul are $2*n + (2 * p + 1)$ numere. Se sție că un singur număr apare de un număr impar de ori (2p + 1)iar celelalte apar de un număr par de oriCum găsiți numărul care apare de un număr impar de ori?+La primul apel, parametrul //str// trebuie sa fie un şir de caractere, ce urmează a fi împartit. Apelurile urmatoare, vor avea în loc de //str//''​NULL''​ conţinuând împarţirea aceluiaşi şir.
  
-Exemplu: +Funcţia va returna la fiecare apel un token(un subsir)ignorând caracterele cu rol de separator aflate în şirul de delimitatori. O dată terminat şirul, funcţia va returna ''​NULL''​.
-        n = 5 și sirul [11, 4, 4, 4, 4, 5, 5, 5] +
-        Răspunsul este 5. +
-          +
-Follow-up 2: Șirul are $2n + 2$ numeren numere apar de câte 2 ori, iar 2 numere sunt uniceCum găsiți cele 2 numer unice?+
  
-Exemplu: +<note important
-        n = 5 și sirul [1, 4, 4, 1, 5, 6] +Implementarea curentă din ''​<string.h>'' ​nu permite folosirea ''​strtok()''​ în paralel pe mai mult de un şir.
-        Numărele unice sunt 5 și 6.  +
- +
-TODO: sursă ​        +
-</spoiler+
- +
-<spoiler Căutare binară pe biți> +
-Realizează o funcție de căutare pe binară, utilizând operații pe biți pentru optimizarea acestei implementări. +
- +
-Follow up: Puteți găsi alt algoritm care să nu se bazeze pe împarțirea vectorului în două și compararea elementului ​din mijloc cu cel cautat? +
- +
-<note> +
- ​Hint:​ +
-[[http://​www.infoarena.ro/​problema/​cautbin | caut bin]] și [[http://​www.infoarena.ro/​multe-smenuri-de-programare-in-cc-si-nu-numai | Multe "​smenuri" ​de programare in C/C++... si nu numai!]]+
 </​note>​ </​note>​
  
-</​spoiler>​+**Exemplu:​**
  
-<spoiler Împărat, bețiv și nervos+<code c
-Împăratul Gigel a primit cadou ** 100 ** de sticle de vin de la un admirator secretAcesta i-a lăsat și o scrisoare în care îi spune despre faptul că este vin nobilînsă a otrăvit o sticlăCine va bea din sticla otrăvită ​** va muri în fix 24h **. +#include <stdio.h> 
 +#include <​string.h>​ 
 +  
 +int main () { 
 +  char str[] = "Uiteasta e un sir."; 
 +  char *p;   
 +  p = strtok(str, " ,​.-"​);​ 
 +  /separa sirul in "​tokeni"​ si afiseaza-i pe linii separate. ​*
 +  while (p != NULL) { 
 +    printf("​%s\n",​ p); 
 +    p = strtok(NULL,​ " ,.-"); 
 +  } 
 +   
 +  return 0; 
 +
 +</​code>​
  
-Gigel dispune de un număr imens de sclavi (precum număr imens de restanțe in Poli), așa că el își va pune sclavii să guste din vin. Se gândește totuși că nu se mai găsesc sclavi pe toate drumurile, așa ca dorește ​** să folosească un număr minim de sclavi ​** (cei care gustă riscă să moară).+**Iesire:**
  
-Ajutați-l pe Gigel să găsească numărul minim de sclavi pe care trebuie să îi foloseacă astfel: +<​code>​ 
-   * pune pe cei **n sclavi** aleși la masă cu câte un pahar în mână, inițial gol +Uite 
-   * poruncește bucătarului să toarne vin în pahare, alegând cui și ce să toarne +asta 
-   * unui sclav i poate turna în pahat vin din oricâte sticle (chiar și simultan) +e 
-   * după ce s-a turnat în **toate** cele n pahare, ei **vor bea toți simultan** +un 
-   * la finalul celor **24h** Gigel trebuie să își dea seama care este sticla otrăvită, altfel se va oftica și va pune 100 de sclavi să guste fiecare din câte o sticlă, iar pe cei care rămân în viață îi va împușca oricum pentru că e nervos+sir 
 +</​code>​
  
-P.S. Apreciați faptul că Gigel încearcă să nu omoare foarte mulți oameni degeaba. Ajutați-l să găsească $numărul minim n de sclavi$ care trebuie să guste din vin.  
  
-Follow-upAceeași problemă, dar pentru n sticle.+==== Exerciții ==== 
 +Exercițiile pentru laborator se găsesc pe [[https://​acs-pclp.github.io/​laboratoare/​07 | PCLP Laborator07Prelucrarea şirurilor de caractere. Funcţii. Aplicaţii]].
  
-TODO: sursă ​       ​ 
  
-<​note>​ +==== Referinţe ====
-Hint: Reprezentarea numerelor în baza 2. +
-</​note>​ +
- +
-</​spoiler>​ +
- +
-<spoiler Jocul NIM>  +
-Se dau n grămezi, fiecare conţinând un anumit număr de pietre. Doi jucători vor începe să ia alternativ din pietre, astfel: la fiecare pas, jucătorul aflat la mutare trebuie să îndepărteze un număr nenul de pietre dintr-o singură grămadă. Câştigătorul este cel care ia ultima piatră. +
-Să se determine dacă jucătorul care ia primele pietre are strategie sigură de câştig. +
- +
-Exemple +
-       ​n ​4, gramezi ​[1 3 5 7], raspuns ​NU +
-       ​n ​3, gramezi ​[4 8 17],  raspuns ​DA +
- +
-<​note>​ +
-Hint: [[http://​www.infoarena.ro/​problema/​nim | nim]] +
-</​note>​+
  
-</spoiler>+  * [[http://​en.wikipedia.org/​wiki/​String_(computer_science)|Wikipedia - String(Computer science)]] 
 +  * [[http://​en.wikipedia.org/​wiki/​String.h|Wikipedia - C string handling]]
  
-<spoiler Sushi> 
-Enunt: [[http://​www.infoarena.ro/​problema/​sushi | sushi ]] 
-</​spoiler>​ 
programare/laboratoare/lab07.1541256535.txt.gz · Last modified: 2018/11/03 16:48 by darius.neatu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0