This shows you the differences between two versions of the page.
programare-cc:laboratoare:07 [2020/11/10 23:50] bogdan.nutu [Probleme] |
programare-cc:laboratoare:07 [2023/12/06 08:48] (current) andrei.simescu [Problema 3] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Laboratorul 07 - Funcții cu pointeri; Șiruri de caractere ===== | ===== Laboratorul 07 - Funcții cu pointeri; Șiruri de caractere ===== | ||
- | **În acest laborator veţi învăţa să:** | + | ==== Problema 1 ==== |
+ | ---- | ||
- | * Trimiteţi parametri prin adresă. | + | Scrieti o functie ce interschimba doua numere intregi. Functia va avea urmatorul antet: |
- | * Utilizaţi şiruri de caractere şi funcţii pentru şiruri de caractere. | + | <code c>void swap(int *pa, int *pb);</code> |
- | * Definiţi funcţii care prelucrează şiruri de caractere. | + | |
- | ==== Pointeri ==== | + | == Date de intrare == |
+ | ---- | ||
- | Atunci când definiţi o variablilă, compilatorul va fi informat despre 2 lucruri: | + | Doua numere intregi, **a** si **b**. |
- | * numele variabilei | + | |
- | * tipul variabilei | + | |
- | De exemplu, în declaraţia: | + | == Date de ieşire == |
- | <code c>int n;</code> | + | ---- |
- | compilatorul va rezerva o zonă de memorie pentru această variabilă, de dimensiune **sizeof(int)** (4 Bytes, pe arhitecturi de 32bit), şi va asocia respectivei zone de memorie numele **n**. | + | |
- | Atunci când se face o atribuire, | + | Valorile **a** si **b**, in urma interschimbarii. |
- | <code c>n = 7;</code> | + | |
- | în respectiva zonă de memorie va fi înscrisă noua valoare, **7**. | + | |
- | Numele variabilelor în C au sens doar în domeniul în care au fost definite acele variabile. Acest domeniu de definiţie al unei variabile poartă numele de **scope**. De exemplu, în programul de mai jos, există 4 variabile **distincte**, toate purtând numele **n**! | + | == Restrictii == |
- | <code c> | + | ---- |
- | #include <stdio.h> | + | |
- | int n; /* Variabila 1 */ | + | * **a** si **b** sunt numere intregi |
- | int f(int n /* Variabila 2 */) { | + | == Exemplu == |
- | n = 42; /* Variabila 2 */ | + | |
- | } | + | |
- | int g() { | + | ---- |
- | int n; /* Variabila 3 */ | + | ^ Intrare ^ Ieşire ^ |
- | n = 42; /* Variabila 3 */ | + | | 2 5 | 5 2 | |
- | } | + | |
- | int h() { | + | ==== Problema 2 ==== |
- | n = 42; /* Variabila 1 */ | + | ---- |
- | } | + | |
- | int main() { | + | Scrieti o functie care numara cate elemente pozitive, cate elemente negative si cate elemente nule se afla intr-un vector. Antetul functiei va fi: |
- | int n; /* Variabila 4 */ | + | <code c>void count(int n, int *v, int *zero, int *poz, int *neg);</code> |
- | n = 42; /* Variabila 4 */ | + | |
- | return 0; | + | |
- | } | + | |
- | </code> | + | |
- | Observăm astfel, că nu putem accesa direct prin nume variabile definite într-o altă funcţie, deoarece numele nu au sens decât în interiorul funcţiei în care au fost declarate. Parametrii formali ai unei funcţii nu sunt altceva decât nişte variabile locale, care în momentul apelului primesc o **copie** a valorilor cu care a fost apelată funcţia. O funcţie de incrementare scrisă astfel: | + | == Date de intrare == |
- | <code c> | + | ---- |
- | void incorrect_increment(int n) { | + | |
- | /* Se va incrementa doar o copie a valorii cu care a fost apelată funcţia! */ | + | |
- | n++; | + | |
- | } | + | |
- | </code> | + | |
- | Nu face decât să incrementeze o variaiblă locală funcţiei, efect ce nu poate fi observat din exterior! | + | * **n** - dimensiunea vectorului |
+ | * **n** numere intregi | ||
- | Pentru a accesa un obiect definit într-o altă funcţie, îl putem referi, totuşi, folosindu-ne de **adresa** acestuia în memorie. Adresele în C poartă numele de pointeri. | + | == Date de ieşire == |
+ | ---- | ||
- | Pentru a obţine adresa unei variabile, vom folosi operatorul **&** pus în faţa numelui variabilei, iar pentru a ne referi la valoarea memorată la o anumită adresă, vom folosi operatorul *. | + | * Numarul de elemente nule |
+ | * Numarul de elemente pozitive | ||
+ | * Numarul de elemente negative | ||
- | Să ne amintim că pentru a citi valori cu ajutorul funcţiei **scanf**, trebuia să precedăm numele variabilelor citite cu operatorul **&**. Acest lucru se întâmplă deoarece din interiorul funcţiei **scanf**, unde se produce citirea, nu sunt accesibile prin nume variabilele definite în funcţia apelantă. | + | == Restrictii == |
+ | ---- | ||
- | O funcţie care incrementează în mod corect o valoare se va scrie astfel: | + | * 0 <= n <= 100 |
- | <code c> | + | |
- | void correct_increment(int* pn) { | + | |
- | /* Vom incrementa aici variabila care este memorată la adresa pn */ | + | |
- | (*pn)++; | + | |
- | } | + | |
- | </code> | + | |
- | iar pentru a incrementa o variabilă numită **n**, va trebui să comunicăm funcţiei de incrementare locaţia acestei variabile astfel: | + | == Exemplu == |
- | <code c> | + | |
- | int main() { | + | |
- | int n; | + | |
- | n = 0; | + | |
- | correct_increment(&n); /* Incrementăm valoarea memorată la adresa variabilei n. */ | + | |
- | return 0; | + | |
- | } | + | |
- | </code> | + | |
- | Putem să ne imaginăm memoria virtuală a calculatorului ca pe un vector foarte lung de Bytes, iar un pointer ca pe un indice în acel vector. Dimensiunea unui pointer (deci a unei adrese de memorie) depinde de tipul sistemului de operare. | + | ---- |
+ | ^ Intrare ^ Ieşire ^ | ||
+ | | 5 \\ -2 -1 0 1 2 | nule: 1 \\ pozitive: 2 \\ negative: 2 | | ||
- | Pe sistemele de operare de 32bit, fie ele Windows, Linux, Mac OS, sau altele, dimensiunea oricărui pointer va fi de **32 biţi**, adică **4 Bytes**, cu valori acceptate între **0** şi **2^32-1**, adică un total de **4GB**. | + | ==== Problema 3 ==== |
+ | ---- | ||
- | ==== Şiruri de caractere ==== | + | * Scrieti o functie pentru stergerea a **n** caractere dintr-o pozitie **p** (data) a unui sir. Se va folosi functia **strcpy**. |
- | Un şir de caractere în limbajul C este o succesiune de octeţi care începe de la o adresă dată şi se termină în mod convenţional cu primul Byte întâlnit care are valoarea numerică **0** (care se poate scrie şi ca '**\0**', caracterul ce are codul **ASCII** egal cu **0**). | + | <code c>char *strdel(char *p, int n); // sterge n caractere din pozitia p</code> |
- | Şirurile de caractere se memorează fie în vectori de tipul **char**, fie în zone de memorie alocate dinamic (despre care vom discuta la laboratorul de alocare dinamică). | + | Pentru a sterge 5 caractere incepand cu pozitia 3 (al 4-lea caracter) din sirul s va trebui sa apelati: **strdel(s + 3, 5)**. |
- | De exemplu, şirul de caractere **"abc"** va fi reprezentat prin vectorul: | + | * Scrieti o functie pentru inserarea unui sir **s** la inceputul sirului dat **p**, folosind functiile **strcpy** si **strcat**. |
- | <code c>{'a', 'b', 'c', '\0'}</code> | + | <code c> char *strins(char *p, const char *s); // insereaza la p, sirul s</code> |
- | şi deci va ocupa **4 Bytes**. | + | |
- | Biblioteca **string.h** conţine o serie de funcţii predefinite pentru prelucrarea şirurilor de caractere. Cele mai importante dintre ele sunt: | + | * Un program care va cauta si inlocui un sir dat cu alt sir (de lungime diferita) intr-un text. Vor fi inlocuite toate aparitiile sirului dat. |
- | <code c>int strcmp(const char* a, const char* b);</code> | + | Se vor folosi functiile definite anterior si functii de biblioteca. |
- | Funcţie care compară două şiruri de caractere, şi întoarce ca rezultat un număr întreg care are valoarea: | + | |
- | * **-1**, dacă primul şir este mai mic lexicografic decât al doilea; | + | |
- | * **0**, dacă cele două şiruri sunt egale; | + | |
- | * **+1**, dacă primul şir este mai mare lexicografic decât al doilea; | + | |
- | <code c>char* strcpy(char* destination, const char* source);</code> | + | Textul se va introduce in program ca sir constant. Sirul cautat si cel cu care va fi inlocuit se citesc. |
- | <code c>char* strncpy(char* destination, const char* source, unsigned int n);</code> | + | |
- | Funcţii care copiază şirul de caractere care începe de la adresa **source** într-o zonă continuă de memorie care începe de la adresa **destination**. Copierea se opreşte după întâlnirea terminatorului de şir în **source** (sau în cazul **strncpy**, după copierea a cel mult **n** caractere, indiferent dacă s-a atins sau nu sfârşitul şirului). Valoarea returnată de funcţie este egală cu parametrul **destination**. | ||
- | <code c>int strlen(source);</code> | + | == Date de intrare == |
- | Funcţie care caculează lungimea unui şir care începe de la adresa **source**. **Atenţie!** Numărarea lungimii se face până la întâlnirea terminatorului de şir, dar acesta nu este inclus în lungimea totală. | + | ---- |
- | <code c>char* strcat(char* destination, const char* source);</code> | + | Trei siruri de caractere: |
- | <code c>char* strncat(char* destination, const char* source, unsigned int n);</code> | + | * textul |
- | Funcţie care copiază în continuarea şirului care începe de la adresa **destination**, conţinutul şirului care începe de la adresa **source**. Copierea se opreşte după întâlnirea terminatorului de şir în **source** (sau în cazul **strncat**, după copierea a cel mult **n** caractere, indiferent dacă s-a atins sau nu sfârşitul şirului). Valoarea returnată de funcţie este egală cu parametrul **destination**. | + | * sirul de inlocuit |
+ | * sirul cu care se inlocuieste | ||
- | <code c>char* strchr(const char* source, const char key); // Prima apariţie a caracterului key.</code> | + | == Date de ieşire == |
- | <code c>char* strrchr(const char* source, const char key); // Ultima apariţie a caracterului key.</code> | + | ---- |
- | <code c>char* strstr(const char* source, const char* key); // Prima apariţie a şirului key.</code> | + | |
- | Acest funcţii sunt folosite pentru a căuta o un caracter sau un şir de caractere în interiorul şirului care începe de la adresa **source**. | + | Textul modificat. |
- | Valoarea returnată este fie adresa apariţiei găsite, daca valoarea există în şir, fie **NULL**, în cazul în care valoarea nu a fost găsită. | + | == Restrictii == |
+ | ---- | ||
- | ==== Citirea şi afişarea şirurilor de caractere ==== | + | * strlen(text) <= 3000 |
+ | * strlen(sir_de_inlocuit) <= 20 | ||
+ | * strlen(sir_cu_care_se_inlocuieste) <= 20 | ||
- | Pentru a citi sau scrie şiruri de caractere se pot folosi funcţiile **scanf** si **printf**, cu variantele lor de utilizare: | + | == Exemplu == |
- | <code c>int scanf("%s", destination); // Observaţi că s este deja o adresă de memorie!</code> | + | |
- | <code c>int printf("%s", source);</code> | + | |
- | Alternativ, se pot folosi şi funcţiile: | + | |
- | <code c>char* gets(char* destination);</code> | + | |
- | <code c>char* fgets(char* destination, int n, FILE* input_file);</code> | + | |
- | <code c>int puts(char* source);</code> | + | |
- | Funcţia **gets** este cauza multor erori grave de programare deoarece nu există nici o protecţie în cazul în care şirul introdus este mai lung decât spaţiul de memorie alocat în program, ceea ce poate duce la suprascrierea accidentală a altor date din program. Pentru a limita citirea la maxim **n** caractere, folosiţi funcţia **fgets**. Fişierul asociat consolei (cel de-al treilea parametru) este în C constanta **stdin**. | + | ---- |
- | + | ^ Intrare ^ Ieşire ^ | |
- | ==== Probleme ==== | + | | ala bala portocala \\ ala \\ lala| lala blala portoclala | |
- | === Problema 1 === | + | ==== Problema 4 ==== |
---- | ---- | ||
- | Scrieti o functie ce interschimba doua numere intregi. Functia va avea urmatorul antet: | + | Scrieti o functie care extrage un subsir de lungime data dintr-o pozitie data a unui sir: |
- | <code c>void swap(int *pa, int *pb);</code> | + | <code c>char* substr(char *src, int n, char *dest); // extrage de la pozitia src in dest n caractere</code> |
+ | |||
+ | Functia are ca rezultat adresa sirului extras. | ||
+ | |||
+ | Pentru a extrage 5 caractere incepand cu pozitia 3 (al 4-lea caracter) din sirul s in sirul p va trebui sa apelati cu parametri: **substr(s + 3, 5, p)**. | ||
== Date de intrare == | == Date de intrare == | ||
---- | ---- | ||
- | Doua numere intregi, **a** si **b**. | + | * sirul de caractere |
+ | * doua numere intregi: **pozitia** in cadrul sirului si **numarul** de caractere extrase | ||
== Date de ieşire == | == Date de ieşire == | ||
---- | ---- | ||
- | Valorile **a** si **b**, in urma interschimbarii. | + | * sirul de caractere extras |
== Restrictii == | == Restrictii == | ||
---- | ---- | ||
- | * **a** si **b** sunt numere intregi | + | * strlen(sir) <= 100 |
+ | * **pozitia** si **numarul** sunt numere intregi valide pentru sirul dat | ||
== Exemplu == | == Exemplu == | ||
Line 161: | Line 133: | ||
---- | ---- | ||
^ Intrare ^ Ieşire ^ | ^ Intrare ^ Ieşire ^ | ||
- | | 2 5 | 5 2 | | + | | abcdefg \\ 2 3 | cde | |
- | === Problema 2 === | + | ==== Problema 5 ==== |
---- | ---- | ||
- | Scrieti o functie care numara cate elemente pozitive, cate elemente negative si cate elemente nule se afla intr-un vector. Antetul functiei va fi: | + | Scrieti o functie care calculeaza panta si ordonata pentru o dreapta data prin 2 puncte. |
- | <code c>void count(int n, int *v, int *zero, int *poz, int *neg);</code> | + | |
+ | Ecuatia dreptei data prin panta si ordonata este **y = m * x + n** | ||
+ | <code c>void panta(int x1, int y1, int x2, int y2, float *m, float *n);</code> | ||
+ | |||
+ | Daca dreapta este verticala, **m** va avea valoarea **INT_MAX** (definita in **limits.h**). | ||
+ | |||
+ | Scrieti un main in care cititi 3 puncte. Primele 2 puncte definesc o dreapta. Verificati daca cel de-al treilea punct dat apartine acestei drepte. | ||
== Date de intrare == | == Date de intrare == | ||
---- | ---- | ||
- | * **n** - dimensiunea vectorului | + | Coordonatele a doua puncte ce determina dreapta si ale unui punct pentru care se verifica daca apartine dreptei. |
- | * **n** numere intregi | + | |
== Date de ieşire == | == Date de ieşire == | ||
---- | ---- | ||
- | * Numarul de elemente nule | + | Unul dintre cele doua mesaje - **DA** sau **NU**, daca punctul apartine dreptei, respectiv daca nu apartine. |
- | * Numarul de elemente pozitive | + | |
- | * Numarul de elemente negative | + | |
== Restrictii == | == Restrictii == | ||
---- | ---- | ||
- | * 0 <= n <= 100 | + | * coordonatele sunt numere intregi valide |
== Exemplu == | == Exemplu == | ||
Line 191: | Line 166: | ||
---- | ---- | ||
^ Intrare ^ Ieşire ^ | ^ Intrare ^ Ieşire ^ | ||
- | | 5 \\ -2 -1 0 1 2 | nule: 1 \\ pozitive: 2 \\ negative: 2 | | + | | 1 1 \\ 2 2 \\ 3 3 | DA | |
+ | ==== Problema 6 ==== | ||
+ | ---- | ||
+ | * Scrieti o functie care extrage urmatorul cuvant format numai din litere mici incepand de la o adresa data. Functia are ca rezultat adresa imediat urmatoare cuvantului extras. Cuvantul extras se va gasi in *word. | ||
+ | <code c>char *next (char *from, char *word);</code> | ||
+ | |||
+ | * Scrieti un program pentru citirea unei linii ce contine cuvinte formate din litere mici si alte siruri si afisarea doar a cuvintelor formate din litere mici. | ||
+ | |||
+ | == Date de intrare == | ||
+ | ---- | ||
+ | |||
+ | Un sir de caractere de lungime **N**. | ||
+ | |||
+ | == Date de ieşire == | ||
+ | ---- | ||
+ | |||
+ | Cuvintele formate din litere mici, in ordine, separate prin spatiu. | ||
+ | |||
+ | == Restrictii == | ||
+ | ---- | ||
+ | |||
+ | * **N** <= 100 | ||
+ | * Se va considera separator de cuvant orice caracter cu exceptia literelor. | ||
+ | |||
+ | == Exemplu == | ||
+ | |||
+ | ---- | ||
+ | ^ Intrare ^ Ieşire ^ | ||
+ | | ( unu, 1 doi DOI trei; "patru" cinCi s@s3) | unu doi trei patru cin i s s | | ||