Sunteți pe pagina 1din 20

05.09.

2018

Programarea Calculatoarelor si CUPRINS


Limbaje de Programare
• Revizuiri ale C-ului standard. Standardele C99/C11
CURS 12  tipul logic headerul <stdbool.h>
 numere complexe headerul <complex.h>
- suport de curs -  tablouri de lungime variabilă
 struct și union anonime
 struct cu membru tablou de mărime variabilă
 headerul <tgmath.h>
Revizuiri ale C-ului standard  funcții inline
 return // __func__
 multithreading
21:40 PCLP CURS 12 2

REVIZUIRI ALE C-ULUI STANDARD. TIPUL LOGIC


STANDARDELE C99/C11 HEADERUL <stdbool.h>

3 21:40 4

bool ePrim( int n ){

Tipul bool
for ( int i = 2; i < n/2; ++i)
if ( n % i == 0 )
return false;
#include <stdio.h> return true;
• Tipul de date logic în C99 este _Bool, care poate avea #include <stdbool.h> }
doar valorile 0 sau 1. bool ePrim( int );
int main( void ) {
• Atribuind orice valoare nenulă unui _Bool el devine 1. for ( int i = 1; i <= 2; ++i ) {
int numar;
printf( "Numar = " ); scanf( "%d", &numar );
• C99 furnizează fișierul header <stdbool.h> care
bool numarPrim = ePrim( numar );
definește macrouri reprezentând tipul de date bool și if ( numarPrim )
valorile sale: true și false. printf( "%d este prim \n\n", numar );
else
printf( "%d nu este prim \n\n", numar );
• Aceste macrouri înlocuiesc true cu 1, false cu 0 și }
bool cu cuvântul cheie _Bool. }
21:40 PCLP CURS 12 5 21:40 PCLP CURS 12 6

Curs PCLP 12 1
05.09.2018

Numere complexe
• Standardul C99 introduce suport pentru numere
complexe și aritmetica lor prin includerea fișierului
header <complex.h>
• Macroul complex este expandat la cuvântul
cheie _Complex:
 tip de date ce alocă un tablou de exact două
NUMERE COMPLEXE elemente, corespunzând
HEADERUL <complex.h>  părții reale și părții imaginare a unui număr
complex,
 o pereche de numere reale în virgulă mobilă.
21:40 7 21:40 PCLP CURS 12 8

Trei tipuri complexe Funcții de bază


• float _Complex (float complex dacă includem Pentru un număr
Funcție
complex întoarce
<complex.h>)
creal() partea reală
• double _Complex (double complex dacă
includem <complex.h>) cimag() partea imaginară

• long double _Complex (long double cabs() modulul


complex dacă includem <complex.h>)
carg() argumentul
• Orice ordine este permisă: long double complex,
complex long double și double complex long conj() complex conjugatul
desemnează același tip.
21:40 PCLP CURS 12 9 21:40 PCLP CURS 12 10

Numere complexe
#include <stdio.h>
#include <complex.h>
int main(void){
Formă număr complex
• Sufixele f și l desemnează tipul float complex
double complex z1 = 1.5 + 1.7*I, z2 = 1.5 - 1.7*I; sau long double complex.
printf( "forma algebrica a numarului complex:
%g%+gi\n", creal(z1), cimag(z1) ); • Macrourile I și _Complex_I reprezintă
printf( "forma trigonometrica: modulul = %g argumentul
= %g\n", cabs(z1), carg(z1) ); Specificator de format  numărul complex (0, 1) notat în matematică cu i.
printf( "complex conjugatul: %g%+gi\n\n",
creal(conj(z1)), cimag(conj(z1)) );  În cazul în care se dorește utilizarea identificatorului
printf( "forma algebrica a numarului complex: %g%+gi\n", I în alt scop se poate invalida macroul I.
creal(z2), cimag(z2) ); #include <complex.h>
printf( "forma trigonometrica: modulul = %g argumentul = #undef I
%g\n", cabs(z2), carg(z2) );
printf( "complex conjugatul: %g%+gi\n\n", creal(conj(z2)),  Putem defini o altă prescurtare pentru _Complex_I,
cimag(conj(z2)) );
} de exemplu J.
21:40 PCLP CURS 12 11 21:40 PCLP CURS 12 12

Curs PCLP 12 2
05.09.2018

Adunare numere
+

#include <stdio.h>
complexe
- Scădere
Numere complexe
#include <complex.h> * Înmulțire

int main( void ) sufix / Împărțire


• Cu numere complexe se pot utiliza și operatorii:
{
float complex z1 = 1.5f + 1.7f*I, z2 = 1.5f - 1.7f*I;
++ -- ! == != && ||
printf( "z1 + z2 = %.4f%+.4fi\n\n", creal( z1 + z2 ),
cimag( z1 + z2 ));
printf( "z1 - z2 = %.4f%+.4fi\n\n", creal( z1 - z2 ),
cimag( z1 - z2 ));
printf( "z1 * z2 = %.4f%+.4fi\n\n", creal( z1 * z2 ), • Programul următor exemplifică acești operatori.
cimag( z1 * z2 ));
printf( "z1 / z2 = %.4f%+.4fi\n\n", creal( z1 / z2 ),
cimag( z1 / z2 ));
printf( "z1 ^ z2 = %.4f%+.4fi\n\n", creal( cpow(z1, z2) ),
cimag( cpow(z1, z2) ));
} Ridicare la putere z1z2
21:40 PCLP CURS 12 13 21:40 PCLP CURS 12 14

#include <stdio.h>
#include <complex.h>
int main(void){
long double complex z1 = 1.5l + 1.7l*I, z2 = 0l - 0l*I;
printf("z1 = %.1f%+.1fi\n\n", creal(z1), cimag(z1));
printf("z1++ = %.1f%+.1fi\n\n", creal(z1++), cimag(z1++));
z1 = 1.5l + 1.7l*I;
printf("z1-- = %.1f%+.1fi\n\n", creal(z1--), cimag(z1--));
printf("z2 = %.1f%+.1fi\n\n", creal(z2), cimag(z2));
printf("!z1: %.1f%+.1fi\n\n", creal(!z1), cimag(!z1));
printf("!z2: %.1f%+.1fi\n\n", creal(!z2), cimag(!z2));
printf("z1 == z2: %.1f%+.1fi\n\n",
creal(z1==z2), cimag(z1==z2));
printf("z1 != z2: %.1f%+.1fi\n\n",
creal(z1!=z2), cimag(z1!=z2));
TABLOURI DE LUNGIME VARIABILĂ
printf("z1 && z2: %.1f%+.1fi\n\n",
creal(z1&&z2), cimag(z1&&z2));
printf("z1 || z2: %.1f%+.1fi\n\n",
creal(z1||z2), cimag(z1||z2));
}
21:40 PCLP CURS 12 15 21:40 16

Tablouri de lungime variabilă Tablouri de lungime variabilă


• Anterior lui C99, tablourile aveau mărime • C99 permite lucrul cu tablouri de mărime
constantă. necunoscută prin folosirea tablourilor de mărime
variabilă (VLA: variable-length array).
• Pentru a gestiona tablouri de mărimi diferite la
fiecare rulare trebuia să folosim alocarea dinamică • Un VLA este un tablou a cărui mărime este
a memoriei: definită pe baza unei expresii care se evaluează în
 funcțiile malloc, calloc, realloc, free.
momentul execuției.
• Programul următor lucrează cu un vector și două
matrice de mărime variabilă, folosind câte o
funcție pentru afișarea lor.
21:40 PCLP CURS 12 17 21:40 PCLP CURS 12 18

Curs PCLP 12 3
05.09.2018

#include <stdio.h> int v[l0]; /* declara vectorul */


printf("\nVector[%d]:\n", l0);
void afisVect(int n, int v[n]);
printf("sizeof(v) = %d octeti\n", sizeof(v));
void afisMat(int l, int c, int a[l][c]);
for(int i = 0; i < l0; ++i)
int main(void) v[i] = i * i;
{ afisVect(l0, v);
int l0; /* marime vector */
int l1, c1, l2, c2; /* linii, coloane matrici */
int a[l1][c1]; /* declara prima matrice */
printf("Marime vector: "); scanf("%d", &l0); printf("\nPrima matrice(%d*%d):\n", l1, c1);
printf("sizeof(prima matrice) = %d octeti\n",
printf("Nr. linii prima matrice: "); scanf("%d", &l1);
sizeof(a));
printf("Nr. coloane prima matrice: "); scanf("%d", &c1);
for(int i = 0; i < l1; ++i)
printf("Nr. linii a 2-a matrice: "); scanf("%d", &l2);
for( int j = 0; j < c1; ++j)
printf("Nr. coloane a 2-a matrice: "); scanf("%d", &c2); a[i][j] = i*j;
afisMat(l1, c1, a);
21:40 PCLP CURS 12 19 21:40 PCLP CURS 12 20

int b[l2][c2]; // declara a 2-a matrice void afisVect(int n, int v[n]){


for(int i = 0; i < n; ++i)
printf("\nA 2-a matrice(%d*%d):\n", l2, c2);
printf(i < n-1?"%d ":"%d\n", v[i]);
printf("sizeof(a 2-a matrice) = %d octeti\n",
}
sizeof(b));

for(int i = 0; i < l2; ++i)


void afisMat(int l, int c, int a[l][c]){
for(int j = 0; j < c2; ++j) for(int i = 0; i < l; ++i) {
b[i][j] = i+j; for (int j = 0; j < c; ++j)

afisMat(l2, c2, b); printf( "%5d", a[i][j] );


printf( "\n" );
}
}
}

21:40 PCLP CURS 12 21 21:40 PCLP CURS 12 22

struct și union anonime


• Un struct sau union anonim sunt imbricate în
struct și/sau union cu nume (e.g. un membru union
fără nume al unui struct).

• Membrii unui struct sau union anonim sunt


considerați a fi membrii ai struct sau union în care
sunt imbricați și
struct ȘI union ANONIME  pot fi accesați direct prin intermediul unui obiect de
tipul celui în care sunt imbricați.

• Următorul cod C11 declară un struct cu un union


anonim și accesează direct membrii din union:
21:40 23 21:40 PCLP CURS 12 24

Curs PCLP 12 4
05.09.2018

struct student
#include <stdio.h>
#include <stdbool.h>
{
char nume[10];
Aplicație cu figuri geometrice
#include <string.h> union { // union anonim

int main(){
bool integralist; • Reluăm o aplicație din Laboratorul 10 (despre
int numarRestante;
struct student s; }; typedef, union, bitfields) care, într-un vector de
}; struct figuri geometrice, citea:
strcpy(s.nume, "Ionescu");
s.integralist = false; //accesare directa a membrilor union  tipul figurii geometrice și apoi
s.numarRestante = 2; //accesare directa a membrilor union
printf("Nume student = %s, are %d restante\n", s.nume,  dacă tipul era cerc, citea valoarea razei,
s.numarRestante);  dacă tipul era triunghi citea cele valorile celor trei laturi,
strcpy(s.nume, "Popescu");  dacă tipul era dreptunghi citea valorile celor două laturi.
s.integralist = true; //accesare directa a membrilor union
printf("Nume student = %s, %s\n", s.nume, • Vom simplifica struct figuri folosind facilitatea
s.integralist?"integralist":"restantier");
} de struct și union anonime imbricate, astfel:
21:40 PCLP CURS 12 25 21:40 PCLP CURS 12 26

Varianta inițială fără struct și union struct figuri {


anonime. Tot ce e colorat nu mai apare în enum tip tip_fig;
varianta cu struct și union anonime. union {
float raza;
struct {
Varianta cu struct și union anonime. float l1, l2, l3;
Codul struct figuri se simplifică } triunghi ;
mult. Aceasta va conduce și la struct {
simplificarea scrierii codului float l1, l2;
programului (vezi în continuare). } dreptunghi ;
} caracteristici;
struct figuri { } ;
enum tip tip_fig;
union {
float raza; // figura este cerc
struct { // triunghi: 3 laturi l1, l2, l3
float l1, l2, l3;// dreptunghi: 2 laturi l1, l2
};
};
} ;
21:40 PCLP CURS 12 27 21:40 PCLP CURS 12 28

printf("CITIREA DATELOR CORESPUNZATOARE FIGURILOR");


#include <stdio.h>
for(i = 0; i < N; ++i){
#include <stdlib.h>
printf("\nFigura[%d]: 0 (cerc), 1 (triunghi), 2
#define N 5 (dreptunghi)? ", i);
enum tip {CERC, TRIUNGHI, DREPTUNGHI}; scanf("%d%*c", &xtip);
struct figuri { if (xtip == CERC){
enum tip tip_fig; figura[i].tip_fig = CERC;
union { printf("Raza cercului (nr real pozitiv) = ");
float raza; // figura este cerc scanf("%f", &figura[i].raza);// &figura[i].caracteristici.raza
struct {// triunghi: 3 laturi l1, l2, l3 }
float l1, l2, l3;// dreptunghi: 2 laturi l1, l2 else if (xtip == TRIUNGHI){
}; figura[i].tip_fig = TRIUNGHI;
}; printf("Latura 1 (nr real pozitiv) = ");
} ; scanf("%g", &figura[i].l1);// &figura[i].caracteristici.triunghi.l1
int main(){ printf("Latura 2 (nr real pozitiv) = ");
enum tip xtip; scanf("%g", &figura[i].l2);// &figura[i].caracteristici.triunghi.l2
struct figuri figura[N]; printf("Latura 3 (nr real pozitiv) = ");
int i; scanf("%g", &figura[i].l3);// &figura[i].caracteristici.triunghi.l3
}
21:40 PCLP CURS 12 29 21:40 PCLP CURS 12 30

Curs PCLP 12 5
05.09.2018

else if (xtip == DREPTUNGHI){ else if (figura[i].tip_fig == TRIUNGHI){


figura[i].tip_fig = DREPTUNGHI;
printf("Figura %d : TRIUNGHI cu laturile = (%g; %g;
printf("Latura 1 (nr real pozitiv) = ");
scanf("%g", &figura[i].l1);//&figura[i].caracteristici.dreptunghi.l1 %g)\n", i, figura[i].l1, figura[i].l2, figura[i].l3);
printf("Latura 2 (nr real pozitiv) = "); } // figura[i].caracteristici.triunghi.l1,
scanf("%g", &figura[i].l2);//&figura[i].caracteristici.dreptunghi.l2 figura[i].caracteristici.triunghi.l2, figura[i].caracteristici.triunghi.l3
}
else if (figura[i].tip_fig == DREPTUNGHI){
else {
printf("Eroare introducere cod figura!"); printf("Figura %d : DREPTUNGHI cu laturile = (%g;
exit(1); %g)\n", i, figura[i].l1, figura[i].l2);
}
} //sfarsit for } // figura[i].caracteristici.dreptunghi.l1,
figura[i].caracteristici.dreptunghi.l2
printf("\nAFISAREA DATELOR CITITE PENTRU FIGURI\n");
for(i = 0; i < N; ++i){ } //sfarsit for
if (figura[i].tip_fig == CERC){ } //sfarsit main
printf("Figura %d : CERC cu raza = %g\n", i,
figura[i].raza); //figura[i].caracteristici.raza
}
21:40 PCLP CURS 12 31 21:40 PCLP CURS 12 32

struct cu membru tablou de mărime


variabilă
• C99 permite să declarăm un tablou căruia să nu îi
specificăm mărimea ca ultimul membru al unui
struct.

• Un astfel de membru tablou se declară însoțit doar


de paranteze drepte ([]):
struct CU MEMBRU TABLOU DE struct PERSOANA {
MĂRIME VARIABILĂ unsigned varsta;
char nume[]; /* membru tablou cu marime flexibilă */
};
21:40 33 21:40 PCLP CURS 12 34

struct cu membrii tablou de mărime struct cu membrii tablou de mărime


variabilă variabilă
• Alocarea spațiului pentru acest tip de struct cu
un membru tablou flexibil: • Operatorul sizeof(struct PERSOANA) ignoră
membrul tablou flexibil.
#define sizeTemp 40 /* dimensiunea estimata a membrului nume */
 Operatorul evaluează mărimea tuturor membrilor
char tempNume[sizeTemp];
structurii cu excepția tabloului flexibil.
struct PERSOANA *ptrPers = NULL;
/* Alocare spatiu pt structura, nume si terminatorul de sir '\0' */  Se alocă un spațiu suplimentar +
ptrPers = (struct PERSOANA *)malloc( strlen(tempNume) + 1 pentru tabloul flexibil.
sizeof(struct PERSOANA) + strlen(tempNume) + 1);
 + 1 este pentru terminatorul ‘\0’.
21:40 PCLP CURS 12 35 21:40 PCLP CURS 12 36

Curs PCLP 12 6
05.09.2018

#include <stdio.h> printf("Numele : ");


#include <stdlib.h> fgets(tempNume, sizeTemp, stdin);
#include <string.h> printf("Varsta : ");
scanf("%d", &tempVarsta);
#define sizeTemp 40 /* dimensiunea estimata a membrului nume */
/* Alocare spatiu pt structura, nume si terminatorul de sir '\0' */
struct PERSOANA {
ptrPers = (struct PERSOANA *)malloc(
unsigned varsta;
sizeof(struct PERSOANA) + strlen(tempNume) + 1);
char nume[]; /* membru tablou cu marime flexibila */
};
strcpy(ptrPers->nume, tempNume);
int main() ptrPers->varsta = tempVarsta;
{
unsigned int tempVarsta; printf("Varsta = %d, Numele = %s\n", ptrPers->varsta,
ptrPers->nume);
char tempNume[sizeTemp]; }
struct PERSOANA *ptrPers = NULL;

21:40 PCLP CURS 12 37 21:40 PCLP CURS 12 38

struct cu membrii tablou de mărime struct cu membrii tablou de


variabilă mărime variabilă
• Sunt multe restricții la utilizarea struct cu membrii
tablouri flexibile. • Orice struct care conține un membru tablou
flexibil nu poate fi membru al unui alt struct
 Membrul tablou flexibil poate fi declarat numai ca
ultim membru al unui struct.  i.e. nu poate fi imbricat în alt struct.
 Orice struct poate conține cel mult un membru
tablou flexibil. • Un struct cu un membru tablou flexibil nu
 Un tablou flexibil nu poate fi unicul membru al unui poate fi inițializat static,
struct.  el trebuie alocat dinamic.
 Acel struct trebuie să mai aibă unul sau mai mulți
membrii ne-flexibili.
21:40 PCLP CURS 12 39 21:40 PCLP CURS 12 40

APLICAȚIE 1: struct cu membrii


tablou de mărime variabilă
• Scrieți un program care folosește un struct pentru
memorarea numelui unui student și a unui vector de
note - pe care acesta le poate lua la laboratorul de
PCLP - și afișează numele studentului și media notelor
acestuia.
• Având în vedere că numărul de note este variabil,
nefiind același pentru fiecare student, vom folosi
pentru membrul vector de note din struct un tablou
de elemente - întregi fără semn - de mărime variabilă.
21:40 PCLP CURS 12 41 21:40 PCLP CURS 12 42

Curs PCLP 12 7
05.09.2018

#include <stdio.h> printf("Nume student : ");


#include <stdlib.h> fgets(ptrStud->nume, 19, stdin);
#include <string.h> /* se elimina '\n' de la finalul sirului */
ptrStud->nume[strlen(ptrStud->nume) - 1] = '\0';
#define sizeTemp 10 /* dimensiunea estimata a membrului note */
printf("Note student %s pana cand se introduce o
struct STUDENT { valoare care nu poate fi nota\n", ptrStud->nume);
char nume[20]; printf("nota[%d] = ", i); scanf("%d", &nota);
unsigned note[]; /* membru tablou cu marime flexibila */ while(nota>=0 && nota<=10) {
}; ptrStud->note[i++] = nota;
media = media + nota;
int main(){ printf("nota[%d] = ", i);
unsigned i = 1, nota;
scanf("%d", &nota);
struct STUDENT *ptrStud = NULL;
}
float media = 0;
printf("\nNumele = %s\n", ptrStud->nume);
/* Alocare spatiu pt structura (nume) si note */
media = media / --i;
ptrStud = (struct STUDENT *)malloc( sizeof(struct printf("Media = %.2f\n", media);
STUDENT) + sizeof(unsigned) * sizeTemp);
}

21:40 PCLP CURS 12 43 21:40 PCLP CURS 12 44

APLICAȚIE 2: tablou de struct cu


membrii tablou de mărime variabilă
• Scrieți un program care folosește un tablou cu
elemente de tip struct care memorează numele
unui student, media notelor și un vector de note - pe
care acesta le poate lua la laboratorul de PCLP - și
afișează numele studentului și media notelor acestuia.
 Având în vedere că numărul de note este variabil,
nefiind același pentru fiecare student, vom folosi pentru
membrul vector de note din struct un tablou de
elemente - întregi fără semn - de mărime variabilă.
21:40 PCLP CURS 12 45 21:40 PCLP CURS 12 46

#include <stdio.h> printf("Numar studenti = "); scanf("%d", &n);


#include <stdlib.h> ptrStud = (struct STUDENT **)malloc(sizeof(struct
#include <string.h> STUDENT *));
#define sizeTemp 10 /* dimensiunea estimata a membrului
note */ for(int j = 0; j < n; ++j){
/* alocam spatiu in heap pt un student */
struct STUDENT {
ptrStud[j] = (struct STUDENT *)malloc(sizeof
char nume[20];
(struct STUDENT) + sizeof(unsigned) * sizeTemp);
float media;
unsigned note[]; /* membru tablou cu marime flexibila */
printf("\nNume student : ");
};
fflush(stdin);
int main()
{ fgets(ptrStud[j]->nume, 19, stdin);
unsigned i, nota, n; ptrStud[j]->nume[strlen(ptrStud[j]->nume) - 1] =
struct STUDENT **ptrStud = NULL; '\0'; /* eliminam ‘\n’ de pe ultima pozitie a sirului */

21:40 PCLP CURS 12 47 21:40 PCLP CURS 12 48

Curs PCLP 12 8
05.09.2018

printf("Note student %s pana cand se introduce o valoare


care nu poate fi nota\n", ptrStud[j]->nume);
i = 1; printf("nota[%d] = ", i); scanf("%d", &nota);
ptrStud[j]->media = 0;
while(nota>=0 && nota<=10) {
ptrStud[j]->note[i++] = nota;
ptrStud[j]->media = ptrStud[j]->media + nota;
printf("nota[%d] = ", i);
scanf("%d", &nota);
}
ptrStud[j]->media = ptrStud[j]->media / --i;
} /* sfarsit for */

printf("\n\nTiparire studenti si mediile lor\n"); HEADERUL <tgmath.h>


for(int j = 0; j < n; ++j){
printf("\nNumele = %s\n", ptrStud[j]->nume);
printf("Media = %.2f\n", ptrStud[j]->media);
}
}
21:40 PCLP CURS 12 49

Headerul <tgmath.h> Headerul <tgmath.h>


• În C11, fișierul header <tgmath.h> • Dacă x este un double, sin(x) va apela sin (care
 include header-ele <math.h> și <complex.h> și are un argument tip double)

 definește câteva macro-uri cu tip generic (_Generic). • Dacă x este un long double, sin(x) va apela
sinl (versiunea pentru long double a lui sin)
• Odată inclus header-ul <tgmath.h>, macro-urile
cu tip generic vor apela funcții corespunzătoare • Dacă x este un număr complex, sin(x) va apela
tipului argumentelor. versiunea corespunzătoarea funcției sin pentru tipul
complex (csin, csinf sau csinl).
• Dacă x este un float, expresia sin(x) va apela
sinf (versiunea pentru tipul float a lui sin). double complex float complex long double complex

21:40 PCLP CURS 12 51 21:40 PCLP CURS 12 52

Headerul <tgmath.h> Headerul <tgmath.h>


• Noul cuvânt cheie _Generic din C11 furnizează • În astfel de cazuri există un macro care apelează
un mecanism pe care îl putem utiliza pentru a crea automat versiunea specifică tipului.
un macro care
• De exemplu, macro-ul ceil apelează funcția
 apelează diferite versiuni specifice anumitor tipuri
de funcții bazate pe tipul argumentului macro-ului.  ceilf dacă argumentul este de tip float
 ceil când argumentul este de tip double
• Multe funcții matematice prevăd versiuni distincte
 ceill când argumentul este de tip long
care au ca argumente tipuri: int, float, double,
double.
long double, float complex, double
complex, long double complex.
21:40 PCLP CURS 12 53 21:40 PCLP CURS 12 54

Curs PCLP 12 9
05.09.2018

• De exemplu, pentru declarațiile: Macro utilizat Apelează


exp(n) exp(n)
#include <tgmath.h>
acosh(f) acoshf(f)
int n;
float f; sin(d) sin(d)
double d; atan(ld) atanl(ld)
long double ld; log(fc) clogf(fc)
float complex fc;
sqrt(dc) csqrt(dc)
double complex dc;
pow(ldc, f) cpowl(ldc, f)
long double complex ldc;
remainder(n, n) remainder(n, n)
• Funcțiile apelate de macro-urile cu tip generic sunt
prezentate în tabelul următor: nextafter(d, f) nextafter(d, f)
21:40 PCLP CURS 12 55 21:40 PCLP CURS 12 56

Macro utilizat Apelează Aplicație 1: macro cu tip generic


nexttoward(f, ld) nexttowardf(f, ld)
• Noul cuvânt cheie din C11: _Generic declară o expresie
copysign(n, ld) copysignl(n, ld)
generică care se translatează în diferite tipuri specifice.
carg(n) carg(n)
• În exemplul următor, macro-ul generic de calcul a
cproj(f) cprojf(f)
rădăcinii cubice cbrt(X)se translatează în funcțiile
creal(d) creal(d) specializate:
cimag(ld) cimagl(ld)  cbrtl(long double),
fabs(fc) cabsf(fc)  cbrtf(float) sau în
carg(dc) carg(dc)  funcția implicită cbrt(double),
cproj(ldc) cprojl(ldc)
• în funcție de tipul efectiv (actual) al parametrului X:
21:40 PCLP CURS 12 57 21:40 PCLP CURS 12 58

Aplicație 2: macro cu tip generic


#include <stdio.h>
#include <tgmath.h>

#define mcbrt(X) _Generic( (X), long double: cbrtl,


• Pe baza modelului de la Aplicația 1 anterioară,
default: cbrt, float: cbrtf)(X) definiți un macro cu tip generic pentru funcția sin.
int main(){
 Ea se va aplica la argumente de tip:
float f = 125.95;
double d = 125.95;  double
long double ld = 125.95;  long double
printf("float: %.2f la 1/3 = %.20f\n\n", f,
 double complex
mcbrt(f));  float complex
printf("double: %.2f la 1/3 = %.20f\n\n", d,  long double complex
mcbrt(d));
printf("long double: %.2lf la 1/3 = %.20lf\n\n", ld,
mcbrt(ld)); • Funcția sin implicită va fi sin(double).
}
21:40 PCLP CURS 12 59 21:40 PCLP CURS 12 60

Curs PCLP 12 10
05.09.2018

#include <stdio.h>
printf("double: sin(%g) = %g\n\n", d, msin(d));
#include <tgmath.h>
#include <complex.h>
printf("long double: sin(%.5lf) = %.20lf\n\n", ld,
#define msin(X) _Generic( (X), long double: sinl, msin(ld));
default: sin, double complex: csin, float complex:
printf("double complex: sin(%g%+gi) = %g%+gi\n\n",
csinf, long double complex: csinl)(X)
creal(dc), cimag(dc), creal(msin(dc)), cimag(msin(dc)) );
int main()
printf("float complex: sin(%g%+gi) = %g%+gi\n\n",
{
creal(fc), cimag(fc), creal(msin(fc)), cimag(msin(fc)));
double d = 0.12595;
long double ld = 0.12595; printf("long double complex:sin(%.5lf%+.1lfi) =
double complex dc = 0.12595 + 0.1*I; %.20lf%+.10lfi\n\n", creal(ldc), cimag(ldc),
float complex fc = 0.12595f + 0.1f*I; creal(msin(ldc)), cimag(msin(ldc)));
long double complex ldc = 0.12595l + 0.1l*I; }
21:40 PCLP CURS 12 61 21:40 PCLP CURS 12 62

FUNCȚII inline

21:40 PCLP CURS 12 63 21:40 64

Funcții inline Funcții inline


• C99 permite declararea (ca în C++) de funcții • Apelul funcțiilor obișnuite consumă timp.
inline, prin plasarea cuvântului cheie inline
• Când declarăm o funcție ca inline programul nu
înaintea declarației funcției:
mai apelează acea funcție.
inline void f();  Compilatorul înlocuiește fiecare apel al unei funcții
inline cu o copie a codului corpului acelei funcții.
• Acest lucru nu are niciun efect asupra logicii
 O funcție inline este asemănătoare unui macro.
programului din punctul de vedere al utilizatorului
acestuia, • Aceasta îmbunătățește timpul de rulare
 dar poate îmbunătății performanța programului.  dar poate mări dimensiunea programului.
21:40 PCLP CURS 12 65 21:40 PCLP CURS 12 66

Curs PCLP 12 11
05.09.2018

Funcții inline #include <stdio.h>

static inline void f1(int *a, int *b); /* interschimbare */


static inline int f2(int x); /* patratul lui x */
• Astfel că vom declara funcții ca inline numai int main() {
int tmp, x = 3, y = 7;
 dacă ele sunt mici și sunt apelate frecvent.
printf("x vechi = %d, y vechi = %d\n", x, y); f1(&x, &y);
• Declarația inline este privită de compilator printf("x nou = %d, y nou = %d\n", x, y);

numai ca o indicație, tmp = f2(x); printf("Patratul lui %d = %d\n", x, tmp);


}
 pe care el poate decide să o ignore. static inline void f1(int *a, int *b) { /* interschimbare */
int tmp; tmp = *a, *a = *b, *b = tmp;
• Să observăm că atunci când declarăm/scriem }
efectiv o funcție inline trebuie să o prefixăm cu static inline int f2(int x) {/* patratul lui x */
cuvântul cheie static sau extern. }
return x*x;

21:40 PCLP CURS 12 67 21:40 PCLP CURS 12 68

O altă variantă pentru o funcție inline


este prefixarea cu cuvântul cheie extern.
#include <stdio.h> #include <stdio.h>

static inline char majuscula(char litera) { extern inline char majuscula(char litera) {
return ((litera >= 'a' && litera <= 'z') ? litera return ((litera >= 'a' && litera <= 'z') ? litera
- ('a' - 'A') : litera ); - ('a' - 'A') : litera );
} }

int main() int main()


{ {
printf("Dati o litera minuscula / majuscula = "); printf("Dati o litera minuscula / majuscula = ");

char a, ch = majuscula(a = getchar()); char a, ch = majuscula(a = getchar());

printf("Litera \'%c\' convertita la majuscula = printf("Litera \'%c\' convertita la majuscula =


\'%c\'\n", a, ch); \'%c\'\n", a, ch);
} }

21:40 PCLP CURS 12 69 21:40 PCLP CURS 12 70

Reluăm prima aplicație cu funcțiile


inline prefixate cu extern.
#include <stdio.h>

extern inline void f1(int *a, int *b); /* interschimbare */


extern inline int f2(int x); /* patratul lui x */

int main() {
int tmp, x = 3, y = 7;

printf("x vechi = %d, y vechi = %d\n", x, y); f1(&x, &y);


printf("x nou = %d, y nou = %d\n", x, y);

tmp = f2(x); printf("Patratul lui %d = %d\n", x, tmp);


}

extern inline void f1(int *a, int *b) { /* interschimbare */


int tmp; tmp = *a, *a = *b, *b = tmp; return // __func__
}

extern inline int f2(int x) {/* patratul lui x */


return x*x;
}
21:40 PCLP CURS 12 71 21:40 72

Curs PCLP 12 12
05.09.2018

Afișarea este aceiași

return fără expresie #include <stdio.h> indiferent de standardul


sub care este compilat.
int f();
• C99 restricționează mai strict ceea ce întorc funcțiile. main(){ printf("f() = %d\n", f()); }
int f() { return; } Compilarea sub standardul C89 nu
• În funcții care întorc valori ne-vide, utilizarea instrucțiunii: produce nicio avertizare și nicio eroare.
return;
 generează mesaje de avertizare sau chiar de eroare.

• Anterior lui C99 deși nu se semnala eroare/avertizare


puteau rezulta comportamente greu de definit ale
programului
 dacă funcția apelantă încerca să utilizeze valoarea Compilarea sub standardul C99/C11
returnată de funcția apelată. produce 2 avertizări și nicio eroare.
21:40 PCLP CURS 12 73 21:40 PCLP CURS 12 74

return în main() Comentariu: //


• În standardul C89, dacă un program ajungea la finalul
funcției main fără a executa o instrucțiune return, • C99 introduce un mod nou de comentariu care
 valoarea returnată către sistemul de operare era este prefixat cu // (ca în C++)
nedefinită.

• În C99 și C11, dacă main returnează tipul int sau nu


are definit tipul returnat, programul returnează 0
către sistemul de operare .
 Dacă tipul nu este precizat (în C99 și C11) se generează
un mesaj de avertizare.
21:40 PCLP CURS 12 75 21:40 PCLP CURS 12 76

Identificatorul predefinit __func__ Headere noi în C11


• __func__ este un identificator predefinit ca un șir • <stdalign.h> furnizează controlul tipurilor de alinieri.
care memorează numele funcției curente.
• <stdatomic.h> furnizează acces neîntreruptibil la
#include <stdio.h>
obiecte în multithreading (multfir).
int f_afis(int);
int main(){ • <stdnoreturn.h> definește macroul noreturn
int i = 10;
printf("in main f = %d\n", f_afis(i));  care se expandează în cuvântul cheie _Noreturn pentru
printf("in main: nume functie = %s\n", __func__); funcții care nu se întorc la apelant.
}
int f_afis(int i){ • <threads.h> bibliotecă pentru thread
printf("in functiei: nume functie = %s\n", __func__);
return i; • <uchar.h> utilitare pentru caracterele UTF-16 și UTF-32
} 16-bit Unicode Transformation Format
21:40 PCLP CURS 12 77 21:40 PCLP CURS 12 78

Curs PCLP 12 13
05.09.2018

Multithreading
• Pentru un programator obișnuit în C, cea mai mare
schimbare adusă de C11 este
 suportul standardizat multithreading.

• Deși conceptul de multithreading nu este nou,


interesul pentru el a crescut puternic datorită
proliferării sistemelor multicore
MULTITHREADING  chiar și smartphon-urile și tabletele sunt acum
multicore.
• Multicore = grup de uniprocesoare (nuclee) integrate la nivelul unui
singur chip (core = nucleu).
21:40 79 21:40 PCLP CURS 12 80

Multithreading Multithreading
• Inițial au apărut procesoare multicore de tip dual • Pentru a profita la maxim de avantajele
core, arhitecturii multicore
 dar la ora actuală sunt extrem de răspândite  trebuie să fim capabili să scriem aplicații multifir.
procesoare quad core sau octa core
 și numărul de nuclee continuă să crească.
• Când un program împarte task-urile în fire
(threads) distincte,
• În sistemele multicore, fiecare nucleu poate  un sistem multicore poate rula aceste fire în
procesa diferite părți ale aplicațiilor paralel.
 și astfel aplicațiile și programele se termină mai
repede.
21:40 PCLP CURS 12 81 21:40 PCLP CURS 12 82

Multithreading Multithreading
• Headerul <threads.h> permite scrierea de cod • Executarea unei aplicații multifir pe un procesor cu
C multifir mult mai portabil decât în standardele C un singur nucleu
anterioare.  poate conduce la un timp mai mare decât simpla
rulare secvențială a firelor.
• Simpla divizare a unui task în două fire și rularea
lor separată pe un sistem cu două nuclee nu va • În continuare prezentăm două programe:
duce la dublarea vitezei de execuție, cu toate că
1. Unul care rulează secvențial două sarcini cu efort
viteza va fi mai mare decât la rularea secvențială a de calcul mare.
firelor task-ului.
2. Altul care rulează aceleași sarcini pe fire de
execuție paralele.
21:40 PCLP CURS 12 83 21:40 PCLP CURS 12 84

Curs PCLP 12 14
05.09.2018

Multithreading
• Vom executa fiecare program pe un computer dual
core cu Windows 7.
• Vom măsura timpul pentru fiecare calcul si timpul
total de calcul pentru ambele programe.
• Ieșirile programelor arată îmbunătățirea timpilor
de rulare când programele multifir se execută pe
sisteme multicore.

21:40 PCLP CURS 12 85 21:40 PCLP CURS 12 86

1. Execuția secvențială a două sarcini cu 1. Execuția secvențială a două sarcini cu


efort de calcul mare efort de calcul mare
• Programul următor utilizează funcția Fibonacci • Vom prelua timpul înainte (în variabilele
varianta recursivă. timpStart1 și timpStart2) și după (în
variabilele timpStop1 și timpStop2) fiecare
• Pentru valori mari, calcularea recursivă a termenilor apel a funcției Fibonacci astfel încât
șirului Fibonacci presupune un timp mare de calcul.
 să putem calcula timpul cerut de fiecare din cele
• Programul solicită ordinul n al termenului Fibonacci două sarcini de calcul.
și calculează pe rând valoarea termenului n din șirul  și timpul total consumat de ambele sarcini de
Fibonacci și a termenului n-1. calcul
 folosind funcția difftime.
21:40 PCLP CURS 12 87 21:40 PCLP CURS 12 88

• Ieșirea arată rezultatele executării programului pe un


computer dual-core cu Windows 7 pe care fiecare
rulare produce aceleași rezultate.
• Rularea programului pe un sistem single core va arăta
rezultate diferite la fiecare rulare
 pentru că de fiecare dată sistemul trebuie să împartă
timpul de rulare a programului nostru cu alte sarcini 2,217933 minute
care rulează în acel moment și care variază de la o
rulare la alta,

• dar totdeauna timpii vor fi mai mari decât la rularea


pe un dual-core.
21:40 PCLP CURS 12 89 21:40 PCLP CURS 12 90

Curs PCLP 12 15
05.09.2018

#include <stdio.h> time_t timpStart1 = time(NULL); // incepe prima sarcina


// de calcul
#include <time.h>
printf("\nCalcul fibonacci(%u)\n", n);
unsigned long long fib(unsigned); unsigned long long int rezultat1 = fib(n);
time_t timpStop1 = time(NULL); // s-a terminat prima
int main( void ) // sarcina de calcul
{
unsigned n; printf("fibonacci(%u) = %llu\n", n, rezultat1);
printf("Timp de calcul = %f minute\n\n",
printf("Ordin termen fibonacci = "); difftime(timpStop1, timpStart1)/60);
scanf("%u", &n);
time_t timpStart2 = time(NULL); // incepe a 2-a sarcina
printf("CALCUL fibonacci(%u) SI fibonacci(%u) // de calcul
APELATE SECVENTIAL\n\n", n, n-1); printf("Calcul fibonacci(%u)\n", n-1);
unsigned long long int rezultat2 = fib(n-1);
time_t timpStop2 = time(NULL); // s-a terminat a 2-a
// sarcina de calcul
21:40 PCLP CURS 12 91 21:40 PCLP CURS 12 92

printf("fibonacci(%u) = %llu\n", n-1, rezultat2); TEMĂ


printf("Timp de calcul = %f minute\n\n",
difftime(timpStop2, timpStart2)/60);
1. Modificați programul anterior pentru a calcula
printf("\nTimp total de calcul = %f minute\n",
secvențial patru termeni Fibonacci (Exemplu: n = 46
=> fib(46), fib(45), fib(44), fib(43)).
difftime(timpStop2, timpStart1)/60);
}
2. Modificați programul anterior pentru a calcula
unsigned long long fib(unsigned n) { secvențial opt termeni Fibonacci (Exemplu: n = 46
if( n < 2 ) => fib(46), fib(45), … fib(39), fib(38)).
return n;
else 3. Modificați programul anterior pentru a calcula
return fib(n-1) + fib(n-2); secvențial un număr oarecare (dat) de termeni
} Fibonacci.
21:40 PCLP CURS 12 93 21:40 PCLP CURS 12 94

2. Execuția paralelă pe mai multe fire a 2. Execuția paralelă pe mai multe fire a
unor sarcini cu efort de calcul mare unor sarcini cu efort de calcul mare
• Programul următor utilizează tot o funcție Fibonacci • Deși timpul total de execuție poate varia de la o
recursivă, rulare la alta,
 dar execută fiecare apel al funcției Fibonacci în fire  timpul total de execuție în fire separate este
separate. totdeauna mai mic decât cel al programului cu
calcul secvențial al termenilor Fibonacci.
• Programul permite rularea pe sisteme multicore
(dual core, quad core sau octo core)
 putând permite și sisteme cu mai multe nuclee
 prin modificarea constantei MAX_FIRE definită la
începutul programului.
21:40 PCLP CURS 12 95 21:40 PCLP CURS 12 96

Curs PCLP 12 16
05.09.2018

Rularea celor 2 programe (secvențial și cu fire) pe un quad core cu Win 8.1

Timp execuție pe două fire


(pe un dual core cu Win 7) =
timpul maxim din cei doi timpi
de execuție ai celor două fire
execuție secvențială
(quad core Win 8.1)

Timp execuție secvențială


(pe un dual core cu Win 7) =
suma celor doi timpi ai celor
2 fire (quad
două calcule core Win 8.1) 4 fire (quad
core Win 8.1)
3 fire (quad
core Win 8.1)
21:40 PCLP CURS 12 97 21:40 PCLP CURS 12 98

#include <stdio.h> int main(void){


#include <threads.h> int numarFire, n, x;
#include <time.h>
printf("numar de fire = ");
#define MAX_FIRE 8 // numarul maxim de fire (pt octo core) scanf("%d", &numarFire);
printf("ordin termen fibonacci = ");
int lansareFib(void*); scanf("%d", &n);
unsigned long long fib(unsigned); x = n;
// tablou pentru memorarea datelor firelor
struct DateFir { // datele corespunzatoare unui fir struct DateFir dateFire[MAX_FIRE];
time_t timpStart; // moment start procesare fir
time_t timpStop; // moment stop procesare fir for(int i = 0; i < numarFire; ++i)
int termen; // termenul fibonacci de calculat pe acest fir dateFire[i].termen = x--; // termenul fibonacci
} ; // pentru fiecare fir

21:40 PCLP CURS 12 99 21:40 PCLP CURS 12 100

// fiecare fir are nevoie de un identificator de fir de tipul // astept terminarea fiecarui calcul
// thrd_t pastram acesti identificatori intr-un tablou cu
for (int i = 0; i < numarFire; ++i)
// index numarul firului
thrd_join(numeFir[i], NULL);
thrd_t numeFir[numarFire];
// in acest punct calculele s-au terminat, firele s-au executat
for(int i = n; i > n - numarFire; --i) // trebuie sa calculam momentul cand s-a declansat executia
printf("fib(%d) ", i); // primului fir si momentul cand ultimul fir si-a incheiat
printf("in fire separate\n\n"); // executia pentru ca prin diferenta lor sa determinam timpul
// total de calcul pentru toate firele
// creare si lansare fir
// determinarea momentului cand a inceput primul fir
for(int i = 0; i < numarFire; ++i) { time_t timpStart = dateFire[0].timpStart;
printf("Start fir de calcul fibonacci(%d)\n", for(int i = 1; i < numarFire; ++i)
dateFire[i].termen); if(dateFire[i].timpStart < timpStart)
if(thrd_create(&numeFir[i], lansareFib, timpStart = dateFire[i].timpStart;
&dateFire[i]) != thrd_success)
printf("Esec creare fir" );
}
21:40 PCLP CURS 12 101 21:40 PCLP CURS 12 102

Curs PCLP 12 17
05.09.2018

// determinarea momentului cand s-a terminat ultimul fir // functie apelata de fiecare fir pt lansare calcul termen Fibo

time_t timpStop = dateFire[0].timpStop; int lansareFib(void *ptr){


// fortam cu un typecast pe ptr la DateFir*
for(int i = 1; i < numarFire; ++i)
if(dateFire[i].timpStop > timpStop) struct DateFir *dataPtr = (struct DateFir *) ptr;
timpStop = dateFire[i].timpStop; dataPtr->timpStart = time(NULL); // momentul inceperii
// calculelor firului curent
// afisarea duratei totale de calcul printf("Calcul fibonacci(%d)\n", dataPtr->termen);
printf("Durata totala de calcul = %f minute\n", // apelam functia fib pt termenul corespunzator firului curent
difftime(timpStop, timpStart)/60); printf("fibonacci(%d) = %lld\n", dataPtr->termen,
} //sfarsit main fib(dataPtr->termen) );

unsigned long long fib(unsigned n){ dataPtr->timpStop = time(NULL); // momentul terminarii


if (n < 2) // calculelor firului curent
return n; printf("Timp de calcul fir curent = %f minute\n\n",
else difftime(dataPtr->timpStop, dataPtr->timpStart)/60);
return fib(n-1) + fib(n-2); return thrd_success;
} } // sfarsit function lansareFib
21:40 PCLP CURS 12 103 21:40 PCLP CURS 12 104

struct DateFir struct DateFir


• Funcția lansareFib pe care fiecare fir o execută  în care vom stoca momentele de timp la care am
primește obiectul DateFir ca argument al său apelat și la care s-a terminat fiecare fir fibonacci.
 prin intermediul pointerului generic ptr. • În primul for din main am încărcat în tabloul
dateFire de tip DateFir valorile termenilor
• Acest obiect conține
Fibonacci corespunzători calculelor fiecărui fir:
 membrul termen cu valoarea termenului
 n, n-1, …., n-numarFire+1
Fibonacci cu care trebuie apelată funcția fib și
 doi membrii (timpStart și timpStop) de tip
time_t

21:40 PCLP CURS 12 105 21:40 PCLP CURS 12 106

thrd_t Crearea și execuția unui fir


• Linia thrd_t numeFir[numarFire]; • Liniile:
for(int i = 0; i < numarFire; ++i) {

• Creează un tablou de obiecte thrd_t. printf("Start fir de calcul fibonacci(%d)\n“,dateFire[i].termen);


if(thrd_create(&numeFir[i],lansareFib,&dateFire[i])!=thrd_success)

• Atunci când creăm un fir, biblioteca multifir }


printf("Esec creare fir" );

creează un identificator al firului și îl stochează • Creează toate cele numarFire fire prin apelul
într-un obiect thrd_t. funcției thrd_create.
• Identificatorul firului poate fi ulterior utilizat cu • Cele trei argumente ale funcției sunt:
diferite funcții multifir. 1. Un pointer la thrd_t pe care thrd_create îl
utilizează pentru a stoca identificatorul firului.
21:40 PCLP CURS 12 107 21:40 PCLP CURS 12 108

Curs PCLP 12 18
05.09.2018

Crearea și execuția unui fir Crearea și execuția unui fir


2. Un pointer la o funcție (lansareFib) care • Funcția thrd_create returnează:
specifică sarcina de executat în cadrul firului.  thrd_success dacă firul este creat,
 funcția trebuie să întoarcă un int și să primească
 thrd_nomem dacă nu există suficientă memorie
un pointer generic (void*) reprezentând
de alocat firului sau
argumentul trimis funcției (în cazul nostru, un
pointer la un obiect DateFir).  thrd_error altfel.
 int reprezintă starea firului la terminare (e.g.
• Dacă firul este creat cu succes, funcția specificată
thrd_success sau thrd_error).
ca al doilea argument (la noi lansareFib)
3. Un pointer void* la argumentul care trebuie începe să se execute în noul fir.
transmis funcției din al doilea argument.
21:40 PCLP CURS 12 109 21:40 PCLP CURS 12 110

Cuplarea firelor Cuplarea firelor


• Pentru a fi siguri că programul nu se termină până • Funcția thrd_join primește:
când firele nu se termină, liniile: 1. un argument de tip thrd_t
for (int i = 0; i < numarFire; ++i)  reprezentând identificatorul firului de cuplat și
thrd_join(numeFir[i], NULL);
2. un pointer la int
 apelează funcția thrd_join pentru fiecare fir
 unde thrd_join poate stoca starea returnată de
care este creat.
fir.
• Aceasta obligă programul să aștepte până când
firele își termină execuția, înainte de a executa
codul rămas neexecutat în main.
21:40 PCLP CURS 12 111 21:40 PCLP CURS 12 112

Funcția lansareFib Funcția lansareFib


• Funcția lansareFib  afișează timpul cât au durat calculele (
difftime(dataPtr->timpStop, dataPtr->
 specifică sarcina de calcul – în cazul nostru apelul timpStart)/60).
funcției fib pentru un calcul recursiv,
 calculează momentele de început și de sfârșit al • Firul se execută până când lansareFib
calculelor firului (dataPtr->timpStart și returnează starea firului:
dataPtr->timpStop)  return thrd_success;
 afișează rezultatul calculelor  la care punct firul se termină.
( fib(dataPtr-> termen)) și

21:40 PCLP CURS 12 113 21:40 PCLP CURS 12 114

Curs PCLP 12 19
05.09.2018

BIBLIOGRAFIE
[1] Deitel, P., Deitel, H., C - How to program, 8th ed., Pearson
Publisher, 2016.

[2] ***, N1570, Standardul C11 - ISO/IEC 9899:2011,


http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

[3] Klemens, B., 21st Century C, 2nd ed., O’Reilly Media Inc., USA,
2015.

21:40 PCLP CURS 12 115

Curs PCLP 12 20

S-ar putea să vă placă și