În acest curs vom învăța despre funcții în C, structurile de date fundamentale precum tablourile și vom explora operațiile pe biți pentru optimizarea programelor.
Funcțiile în C sunt blocuri de cod care pot fi apelate oriunde în program. Ele ajută la structurarea codului și la evitarea repetițiilor.
O funcție trebuie declarată înainte de a fi utilizată. Aceasta are o semnătură, un tip de retur și o listă de parametri.
#include <stdio.h>
// Declarația funcției
int aduna(int a, int b);
int main() {
int rezultat = aduna(5, 7);
printf("Suma: %d\n", rezultat);
return 0;
}
// Definiția funcției
int aduna(int a, int b) {
return a + b;
}
În C, parametrii pot fi transmiși prin două moduri: prin valoare și prin referință (prin pointeri).
1. Transmitere prin valoare
Când se transmite prin valoare, o copie a variabilei este creată, iar modificările făcute în funcție nu afectează variabila originală.
#include <stdio.h>
void modifica(int x) {
x = 10;
}
int main() {
int a = 5;
modifica(a);
printf("Valoarea lui a: %d\n", a); // Rămâne 5
return 0;
}
2. Transmitere prin referință (pointeri)
Prin utilizarea pointerilor, putem modifica direct valoarea variabilei originale.
#include <stdio.h>
void modifica(int *numar) {
*numar = 10;
}
int main() {
int x = 5;
modifica(&x);
printf("Noua valoare: %d\n", x); // Devine 10
return 0;
}
O funcție recursivă este o funcție care se apelează pe ea însăși pentru a rezolva o problemă de tip divide et impera.
#include <stdio.h>
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
int main() {
int numar = 5;
printf("Factorialul lui %d este %d\n", numar, factorial(numar));
return 0;
}
Funcțiile inline sunt utilizate pentru a reduce overhead-ul apelului de funcție.
#include <stdio.h>
inline int patrat(int x) {
return x * x;
}
int main() {
printf("Patratul lui 4: %d\n", patrat(4));
return 0;
}
Funcțiile variadice pot accepta un număr variabil de argumente (de exemplu, `printf`).
#include <stdio.h>
#include <stdarg.h>
void afiseaza_numere(int numar, ...) {
va_list args;
va_start(args, numar);
for (int i = 0; i < numar; i++) {
printf("%d ", va_arg(args, int));
}
va_end(args);
printf("\n");
}
int main() {
afiseaza_numere(3, 10, 20, 30);
return 0;
}
Clasele de stocare determină unde și cât timp este păstrată o variabilă în memorie. Tipurile principale sunt:
Implicit, orice variabilă declarată într-o funcție este `auto`, ceea ce înseamnă că este stocată pe stiva funcției și este ștearsă automat la ieșirea din funcție.
void functie() {
auto int x = 10; // Variabilă locală
}
Variabilele `static` rețin valoarea între apelurile funcției și sunt vizibile doar în fișierul curent.
#include <stdio.h>
void contor() {
static int x = 0;
x++;
printf("x: %d\n", x);
}
int main() {
contor();
contor();
contor();
return 0;
}
Acest modificator sugerează compilatorului să stocheze variabila într-un registru al CPU pentru acces rapid (dar compilatorul poate ignora această sugestie).
void functie() {
register int x = 10;
}
Variabilele `extern` sunt declarate într-un fișier și definite în alt fișier. Utilizate pentru partajarea datelor între module.
// fisier1.c
#include <stdio.h>
extern int global_var;
void afisare() {
printf("Valoare global_var: %d\n", global_var);
}
// fisier2.c
#include <stdio.h>
int global_var = 10;
int main() {
afisare();
return 0;
}
Tablourile sunt structuri de date care stochează mai multe elemente de același tip într-o zonă contiguă de memorie.
Vectorii sunt tablouri unidimensionale. Accesarea elementelor se face prin indici, începând de la 0.
#include <stdio.h>
int main() {
int numere[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
printf("%d ", numere[i]);
}
return 0;
}
Matricile sunt tablouri bidimensionale, utile pentru reprezentarea unor structuri mai complexe, cum ar fi tabele sau imagini.
#include <stdio.h>
int main() {
int matrice[2][2] = {{1, 2}, {3, 4}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
printf("%d ", matrice[i][j]);
}
printf("\n");
}
return 0;
}
Operațiile pe biți sunt utile pentru optimizarea programelor și interacțiunea cu hardware-ul la nivel scăzut.
Operatorii pe biți sunt folosiți pentru manipularea individuală a biților dintr-un număr întreg.
& // AND logic pe biți
| // OR logic pe biți
^ // XOR logic pe biți
~ // Complement (NOT)
<< // Shift la stânga
>> // Shift la dreapta
Exemplu de utilizare a operatorilor pe biți pentru manipularea valorilor numerice:
#include <stdio.h>
int main() {
int x = 5, y = 3;
printf("AND: %d\n", x & y);
printf("OR: %d\n", x | y);
printf("XOR: %d\n", x ^ y);
printf("Shift Left: %d\n", x << 1);
printf("Shift Right: %d\n", x >> 1);
return 0;
}
Operațiile pe biți sunt foarte eficiente în optimizarea codului. De exemplu, în loc să înmulțim cu 2, putem folosi `x << 1`, ceea ce este mai rapid.
#include <stdio.h>
int main() {
int x = 5;
printf("x * 2: %d\n", x << 1);
return 0;
}