Şiruri de caractere (2) Funcţiile pentru prelucrarea şirurilor de caractere din biblioteca string.h se referă la compararea a două şiruri, copiere de şiruri, determinarea lungimii şirurilor şi altele. Funcţia unsigned strlen (const char *s); returnează lungimea unui şir de caractere. Lungimea şirului este numărul de caractere proprii, care intră în compunerea şirului respectiv, fără caracterul NUL. Exemple: 1) char *const p = Acesta este un sir ; unsigned n; n = strlen (p); n = 18 şi reprezintă numărul caracterelor proprii din compunerea şirului spre care pointează p. 2) char tab [ ] = Acesta este un sir ; int n; n = strlen (tab); 3) int n; n = strlen ( Acesta este un sir ); Funcţia pentru copierea unui şir de caractere este: char *strcpy (char *dest, const char *sursa); Funcţia copiază şirul de caractere spre care pointează sursa în zona de memorie a cărei adresă de început este valoarea lui dest. Funcţia copiază şi caracterul NUL de la sfârşitul şirului. La revenire, funcţia returnează adresa de început a zonei în care s-a transferat şirul, adică valoarea lui dest. Această valoare este pointer spre caractere, deci tipul returnat este char*. 1
Parametrul sursă este declarat cu modificatorul const, deoarece funcţia nu are voie să modifice şirul care se copiază; în schimb, parametrul destinaţie nu este declarat cu modificatorul const, deoarece funcţia strcpy modifică zona spre care pointează destinaţia. Exemple: 1) char tab [ ] = Acest sir se copiaza ; char t [sizeof (tab)]; strcpy (t, tab); 2) char t [100]; strcpy (t, Acest sir se copiaza ); 3) char *p = Acest sir se copiaza ; char t [100]; char *q; q = strcpy (t, p); Se pot copia maxim n caractere ale unui şir dintr-o zonă de memorie în alta folosind funcţia: char *strncpy (char *dest, const char *sursa, unsigned n); Dacă n > lungimea şirului spre care pointează sursa, atunci toate caracterele şirului respectiv se transferă în zona spre care pointează dest; altfel se copiază numai primele n caractere ale şirului. În rest, acelaşi efect ca şi strcpy. Funcţia care concatenează două şiruri are următorul prototip: char *strcat (char *dest, const char *sursa); Funcţia copiază şirul de caractere din zona spre care pointează sursa, în zona de memorie care urmează imediat după ultimul caracter propriu al şirului spre care pointează dest. Ultimul caracter în şirul rezultat este NUL. Funcţia returnează valoarea lui dest (adresa şirului rezultat). Exemplu: 2
char tab1[100] = Limbajul C ; char tab2[ ] = este C incrementat ; strcat (tab1, ); strcat (tab1, tab2); Copierea unui subşir dintr-un şir în ali şir se poate realiza folosind funcţia: char *strncat (char *dest, const char *sursa, unsigned n); În acest caz se concatenează la sfârşitul şirului, spre care pointează dest, cel mult n caractere ale şirului spre care pointează sursa. Dacă n > lungimea şirului spre care pointează sursa, atunci se concatenează întregul şir, altfel numai primele n caractere ale acestuia. Compararea şirurilor este o altă operaţie des utilizată la şiruri. Ea poate fi realizată prin următoarele funcţii: int strcmp (const char *s1, const char *s2); int strncmp (const char *s1, const char *s2, unsigned n); int stricmp (const char *s1, const char *s2); Dacă sir1 este şirul de caractere spre care pointează s1 şi dacă sir2 este şirul de caractere spre care pointează s2, primul format al funcţiei de comparare returnează valoare negativă, dacă sir1 < sir2, zero, dacă sir1 = sir2 şi o valoare pozitivă, dacă sir1 > sir2. Facem observaţia că şirul s1<s2, dacă există un indice i astfel încât s1[i]<s2[i] şi s1[j]=s2[j], pentru orice j=0,1,2,,i-1. În cazul celui de-al doilea format, se compară cele două şiruri pe lungimea n. Dacă minimul dintre lungimile celor două şiruri este mai mic decât n, atunci funcţia strncmp realizează aceeaşi comparaţie ca şi funcţia strcmp. Dacă la o comparare de şiruri de caractere nu este necesar a se face distincţie între literele mari şi literele mici ale alfabetului, se poate folosi funcţia având prototipul în forma a treia prezentată anterior. 3
Exemplu: char *sir1 = ABC ; char *sir2 = abc ; int i; Apelul i = strcmp(sir1, sir2); returnează o valoare negativă, deoarece literele mari au coduri ASCII mai mici decât literele mici (A are codul 65, iar a are codul 97), deci ABC < abc. Iar, apelul i = stricmp(sir1, sir2); returnează 0. Funcţia având prototipul char *strchr(char * s, char c); returnează prima apariţie (adresa) a caracterului c în şirul s, respectiv NULL dacă c nu este în s. Funcţia char *strstr(char *s, char *ss); returnează poziţia primei apariţii a şirului ss în şirul s, respectiv NULL, dacă ss nu este în s. return contor; Funcţii care lucrează cu şiruri de caractere (completare curs ) Incluse in biblioteca <string> 4
char *strtok(char *sir1,char *sir2); Efect: separă şirul sir1 în entităţi delimitate de unul sau mai multe caractere din şirul sir2 (acestea având rol de separatori). Apelul funcţiei se face prima dată sub forma strtok(sir1,sir2) - funcţia întoarce adresa primului caracter al primei entităţi - şi a doua oară sub forma strtok(null,sir2) şi funcţia întoarce adresa primului caracter al următoarei entităţi şi după el este adăugat caracterul nul. Când şirul iniţial nu mai conţine entităţi, intoarce adresa nulă. Exemplu: char linie[128], * cuv; char *sep=.,;\t\n // şir de caractere separator gets(linie); cuv=strtok (linie,sep); // primul cuvant din linie while ( cuv!=null) puts (cuv); cuv=strtok(0,sep); // urmatorul cuvant din linie In stdlib.h sunt declarate câteva funcţii folosite pentru extragerea unor numere dintr-un text (dintr-un sir de caractere) şi care ilustrează o altă soluţie pentru funcţii care primesc o adresă într-un şir, extrag un subşir şi modifică adresa în cadrul şirului (după subşirul extras). Este vorba de funcţiile: double strtod (char *s, char **p); - string to double long strtod (char *s, char **p); - string to long care extrag, la fiecare apel, un subşir care reprezintă un număr şi au două rezultate: valoarea numărului în baza zece şi adresa imediat următoare numărului extras în şirul analizat. Exemplu: char * str =" 1 2.2 3.333 44e-1 "; char * p = str; 5
double d; do d = strtod (p,&p); printf ("%lf\n", d); while (d!= 0); char *strupr(char *s) Efect: transformă un şir de caractere din litere mici în litere mari. Restul caracterelor rămân nemodificate. char *strlwr(char *s) Efect: transformă un şir de caractere din litere mari în litere mici. Restul caracterelor rămân nemodificate. Funcții incluse in biblioteca <stdlib.h> int atoi(char *s) Efect: transformă un şir de carcatere într-un întreg (int). Exemplu: int n; char *s= 1234.56 ; n=atoi(s); // va afisa 1234 long atol(char *s) Efect: transformă un şir de carcatere într-un întreg (long). double atof(char *s) Efect: transformă un şir de carcatere într-un număr real. Exemplu: float n; 6
char *s= -4521234.56 ; n=atof(s); // va afisa -4521234.56 char *itoa(int val, char *sir, int baza) Efect: transformă un numar întreg (int) într-un şir de caractere. Baza reprezintă baza in care este scris noul număr. Exemplu: int n=12345; char s[20]; itoa(n,s,10); // va afisa sirul 12345 char *ltoa(long val, char *sir, int baza) Efect: transformă un numar întreg (long) într-un şir de caractere. char *ultoa(unsigned long val, char *sir, int baza) Efect: transformă un numar întreg (unsigned long) într-un şir de caractere. Funcţii care lucrează cu caractere incluse in biblioteca <ctype.h>. Testează dacă un caracter primit ca parametru îndeplineşte o condiţie. Returnează 0 dacă acel caracter nu indeplineşte condiţia şi valoare diferită de 0 dacă o îndeplineşte. int isalnum(int c); Efect:testează dacă un caracter este literă sau cifră int isalpha(int c); Efect: testează dacă un caracter este literă 7
int isdigit(int c); Efect: testează dacă un caracter este cifră int islower(int c); Efect: testează dacă un caracter este literă mică int isupper(int c); Efect: testează dacă un caracter este literă mare int isspace(int c); Efect: testează dacă un caracter este spaţiu int isxdigit(int c); Efect: testează dacă un caracter este cifră în baza 16 int toupper(int c); Efect: transformă un caracter care este litera mică în literă mare int tolower(int c); Efect: transformă un caracter care este litera mare în literă mică Exemplu: 1. Pornind de la un şir citit de la tastatură, să se construiască şi să se tipărească: şirul care conţine cifrele din cel iniţial; şirul care conţine minusculele vocalelor din cel iniţial; şirul invers celui iniţial; să se verifice dacă şirul iniţial este palindrom. 8
#include <stdio.h> #include <conio.h> #include <string.h> #include <ctype.h> #define LUNGS 81 //variabile globale char sir[lungs],aux[lungs]; void constr1(void); void constr2(void); void constr3(void); int main( ) puts("*** sirul de prelucrat:"); gets(sir); constr1(); constr2(); constr3(); return 0; void constr1( ) int i,j; for (i=j=0;i<strlen(sir);i++) if(isdigit(sir[i])) // conditia echivalenta cu '0'<=sir[i]<='9' aux[j++]=sir[i]; aux[j]='\0'; printf("sirul de cifre:%s\n",aux); void constr2( ) int i,j; char c; 9
for (i=j=0;i<strlen(sir);i++) if(c=tolower(sir[i]),c=='a' c=='e' c=='i' c=='o' c=='u') aux[j++]=c; aux[j]='\0'; printf("sirul de vocale:%s\n",aux); void constr3( ) int i,lung; for (i=0,lung=strlen(sir);i<lung;i++) aux[i]=sir[lung-1-i]; aux[i]='\0'; printf("sirul invers:%s\n",aux); if(strcmp(sir,aux)==0) puts("*** Este palindrom"); else puts("*** Nu este palindrom"); STRUCTURI ŞI TIPURI DEFINITE DE UTILIZATOR Definirea structurilor O structură este o colecţie de valori eterogene, stocate într-o zona compactă de memorie. Componentele unei structuri, denumite câmpuri, sunt identificate prin nume simbolice, denumite selectori. Câmpurile unei structuri pot fi de orice tip, simplu sau 10
derivat, dar nu void sau funcţie. Declararea structurilor se face folosind cuvântul cheie struct: struct nume_structura lista_câmpuri //declaratii de forma tip_câmpi nume_câmpi; lista_declaratori; //variabile de tip structura unde nume_structura sau lista_declaratori pot lipsi, dar nu simultan. Dacă se precizează nume_structura, atunci înseamnă că se face definirea tipului struct nume_structura, care poate fi apoi folosit pentru declararea de variabile, ca tip de parametri formali sau ca tip de rezultat returnat de funcţii. Exemple: 1. Definirea unei date de tip structură în care se precizează şi nume_structura şi lista_declaratori. struct coordonate float x,y; punct,*ppunct,puncte[20]; //variabile Pentru această dată mai pot fi definite şi alte varibile, după necesităţi: struct coordonate alt_punct=1,4.5, alte_puncte[10]=2,4,8,9,7, mai_multe_puncte[25]=1,2,3,4; Variabilele de tip structură se pot iniţializa la declarare; iar câmpurile neprecizate sunt implicit egale cu 0. 2. Exemplu de dată de tip structură în care lipseşte lista_declaratori. În acest caz variabilele de tip structură se vor declara separat de definirea structurii. 11
struct persoana char nume[20]; int varsta; ; struct persoana pers="ion Ionescu",21, *ppers, persoane[12]; 3. Definirea unei date de tip structură în care se precizează lista_declaratori, iar nume_structura lipseşte. struct char titlu[20],autor[15],editura[12]; int an_aparitie; carte,biblioteca[1000]; În acest caz declaraţiile de variabile pot fi făcute doar la definire. Un câmp al unei structuri poate fi de tip structura, dar nu aceeaşi cu cea definită. Totuşi, se poate declara în cadrul unei structuri un câmp pointer la structura definită. Acest tip de date va fi utilizat la implementarea listelor şi poartă numele de structuri recursive. Exemple: 1. struct persoana char nume[20]; struct int zi,an,luna data_nasterii; //Câmp de tip structura p; 12
2. struct nod_lista tip_info info; struct nod_lista * urm; //Câmp pointer la structura definita ; Numele unei structuri se depun într-un spaţiu de nume diferit de cel al numelor de variabile, de aceea se pot declara variabile şi tipuri structură cu acelaşi nume. Mai mult, se pot declara tipuri structuri care au nume de câmpuri identice. Nu este indicat a se utiliza astfel de declaraţii. Operaţii cu structuri Selectarea unui câmp al unei variabile structuri se realizează folosind operatorul selectie.. variabila_structura.nume_câmp Câmpul selectat se comportă ca o variabilă de acelaşi tip, deci i se pot aplica aceleaşi prelucrări ca oricărei variabile de tipul respectiv. Selecţia câmpurilor pentru variabila p de tip persoana declarată în exemplul precedent se realizează prin: p.nume //tablou de caractere p.nume[0] //primul caracter din nume p.nume[strlen(p.nume)-1] //ultimul caracter din nume p.data_nasterii.an p.data_nasterii.luna 13
p.data_nasterii.an Exemplu: Fie declaraţia de tip structură student cu câmpurile: nume, nr_student şi medie: struct student char *nume; int nr_student; float medie; ; pentru care declarăm două variabile : struct student temp, grupa[100]; Fie declarațiile: #define NR_STUDENTI 100 struct student char *nume; int nr_student; float medie; ; struct student temp, grupa [NR_STUDENTI]; În acest caz, putem avea instrucţiuni de asignare cum ar fi: temp.medie = 4.00; temp.nume = "Ionescu"; temp.nr_student = 1023; În continuare, scriem o funcţie care numără studenţii cu media 4.00: 14
int esec(struct student grupa[]) int i, contor = 0; for (i = 0; i < NR_STUDENTI; ++i) contor += grupa[i].medie == 4.00; return contor; Deoarece structurile se prelucrează frecvent prin intermediul pointerilor, a fost introdus operatorul ->, acesta combină operatorul de indirectare * cu operatorul de selecţie.. Cele două expresii de mai jos sunt echivalente: (*variabila_pointer).nume_câmp variabila_pointer->nume_câmp Pentru exemplul anterior, fie următoarele declaraţii şi asignări: struct student temp, *p = &temp; temp.medie = 10.00; temp.nume = "Ionescu"; temp.nr_student = 1204; Expresiile echivalente şi valorile corespunzătoare sunt următoarele: Expresie echivalentă Expresie Valoare temp.medie p -> medie 10.00 temp.nume p -> nume Ionescu temp.nr_student p-> nr_student 1024 (*p).nr_student p-> nr_student 1024 Unei variabile de tip structură i se pot aplica operatorii de adresare şi de dimensiune: & și 15
sizeof. O variabilă structură poate fi iniţializată la declarare prin precizarea între a valorilor câmpurilor; cele neprecizate sunt implicit 0. O variabilă structură poate fi copiată în altă variabilă de acelaşi tip. Cu declaraţiile de mai sus, avem : printf("%d %d\n",sizeof(pers),sizeof(struct persoana)); ppers=&pers; persoane[0]=*ppers; O variabilă structură nu poate fi citită sau scrisă direct, ci prin intermediul câmpurilor. Funcţiile pot avea parametrii de tip structură, iar tipul rezultatului returnat de o funcţie poate fi structură. Exemple: 1. Să se proiecteze o structură de date pentru reprezentarea numerelor complexe şi să se scrie funcţii care realizează: operaţii de calcul al modului unui număr complex, afişarea numerelor complexe, adunarea şi înmulţirea numerelor complexe reprezentate astfel: struct complex float coef_real; float coef_imag; ; #include <stdio.h> #include <math.h> struct complex float x, y; ; 16
struct complex nr1,nr2,nr; float modul(struct complex z) float m; m=sqrt(z.x*z.x+z.y*z.y); return m; void scrie(struct complex z) printf("a+bi=%f + i*(%f) \n",z.x,z.y); void aduna(struct complex z1,struct complex z2,struct complex *z) (*z).x=z1.x+z2.x; z->y=z1.y+z2.y; /* alta funcţie de adunare struct complex adu(struct complex z1,struct complex z2) struct complex c; c.x=z1.x+z2.x; c.y=z1.y+z2.y; return c; */ struct complex inmultire(struct complex z1, struct complex z2) struct complex c; c.y=z1.x*z2.y+z1.y*z2.x; c.x=z1.x*z2.x-z1.y*z2.y; 17
return c; int main() printf("coef.real="); scanf("%f",&nr1.x); printf("coef.imag="); scanf("%f",&nr1.y); scrie(nr1); printf("modulul = %g\n",modul(nr1)); printf("coef.real=");scanf("%f",&nr2.x); printf("coef.imag=");scanf("%f",&nr2.y); scrie(nr2); printf("modulul = %g\n",modul(nr2)); printf("suma \n"); aduna(nr1,nr2,&nr); scrie(nr); // nr=adu(nr1,nr2); scrie(nr); printf("produs \n"); nr=inmultire(nr1,nr2); scrie(nr); return 0; 2. Se citesc informaţii despre n studenţi ce conţin nume, nota1, nota2, nota3 și nota4. Să se afişeze: - studenţii în ordine alfabetică şi mediile lor; - studenţii în ordinea descrescătoare a mediilor #include <stdio.h> #include <string.h> 18
struct STUDENT char nume[40]; int note[4]; float media; s[100]; int n; void citire(struct STUDENT []); void ordalfa(struct STUDENT []); void ordmedie(struct STUDENT []); void afis(struct STUDENT []); int main() citire(s); afis(s); printf("afisare alfabetica\n"); ordalfa(s); afis(s); printf("afisare in ordinea mediilor \n"); ordmedie(s); afis(s); printf("hello world!\n"); return 0; void citire(struct STUDENT s[]) int i,j; do printf("nr. studenti="); scanf("%d",&n); while (n<1 n>100); for(i=0;i<n;i++) printf("informatii pentru studentul %d \n",i+1); printf("numele : "); scanf("%40s",s[i].nume); // gets(s[i].nume); fflush(stdin); s[i].media=0; for(j=0;j<4;j++) printf("nota %d =",j+1); scanf("%d",&s[i].note[j]); s[i].media+=s[i].note[j]; s[i].media/=4; 19
void ordalfa(struct STUDENT s[]) int i,ind; struct STUDENT aux; do ind=0; for(i=0;i<n-1;i++) if (strcmp(s[i].nume,s[i+1].nume)>0) aux=s[i]; s[i]=s[i+1]; s[i+1]=aux; ind=1; while(ind); void ordmedie(struct STUDENT s[]) int i,ind; struct STUDENT aux; do ind=0; for(i=0;i<n-1;i++) if (s[i].media<s[i+1].media s[i].media==s[i+1].media &&strcmp(s[i].nume,s[i+1].nume)>0) aux=s[i]; s[i]=s[i+1]; s[i+1]=aux; ind=1; while(ind); void afis(struct STUDENT s[]) int i; for(i=0;i<n;i++) printf("%40s are media %f\n",s[i].nume,s[i].media); 20
Declaraţii de tip (Typedef) În limbajul C se poate atribui un nume unui tip, indiferent dacă el este un tip predefinit sau unul utilizator, folosind o construcţie de forma: typedef tip nume_tip; unde tip este un tip predefinit sau un tip utilizator; iar nume_tip este numele care se atrinuie tipului definit de tip. După ce s-a atribuit un nume unui tip, numele respectiv poate fi utilizat pentru a declara date de acel tip, exact la fel cum se utilizează în declaraţii cuvântul cheie ale tipurilor predefinite: int, char, float, etc. Exemple: 1. typedef int INTREG ; Declaraţia INTREG x ; este identică cu int x ; 2. typedef int CULOARE; CULOARE rosu, verde, albastru; 3. typedef char * string; typedef int lungime; typedef float vector[10]; typedef double (*PFD)(double); După aceste redenumiri, putem face declaraţiile: lungime l1, l2; string s1 = "abc", s2 = "xyz"; 21
vector x; PFD f; Aceste declaraţii sunt echivalente cu: int l1, l2; char * s1 = "abc", s2 = "xyz"; float x[10]; double (*f)(double); /* pointer la o funcţie ce returnează tipul "double" */ 4. typedef struct data_calenadaristica int zi; char luna[15]; i int an; DC; Declaraţia DC data_nasterii, data_angajarii; este identică cu struct data_calenadaristica data_nasterii, data_angajarii; Un caz particular al tipului întreg este tipul enumerare. Pentru declararea tipurilor enumerare se foloseste cuvântul rezervat enum. Tipul enumerare declară date simbolice, cărora li se asociază coduri numerice de tip întreg, astfel: enum nume_tip lista_constante_simbolice nume_tip poate lipsi din declaraţie. Acesta va implica denumirea mulţimii, enumerarea elementelor (numite enumeratori), ca elemente ale mulţimii. Exemplu: enum zile luni, marti, miercuri, joi, vineri, sambata, duminica; Aceasta declaraţie creeaza tipul utilizator enum zile. Cuvântul rezervat enum 22
este urmat de identificatorul "zile". Enumeratorii sunt identificatorii luni, marti, miercuri, joi, vineri, sambata, duminica. Acestea sunt constante de tip int. Prin convenţie, primul este 0, apoi restul sunt incrementaţi. Declararea variabilelor de tip enum zile se face astfel: enum zile zi1, zi2; Variabilele zi1 şi zi2 pot lua ca valori elemente ale acestui tip. De exemplu, zi1 = miercuri; va asigna variabilei zi1 valoarea miercuri. Instrucţiunea if (zi1 == zi2)... va testa dacă valoarea variabilei zi1 este egală cu valoarea variabilei zi2. Enumeratorii pot fi iniţializaţi. De asemenea, putem declara variabilele în timpul definirii tipului enum. 23