Ce face void * (pointer generic)
void * este pointerul „fără tip”: poate primi adresa oricărei variabile, fără conversie explicită. Funcțiile generice din biblioteca standard îl folosesc: malloc returnează void *, iar memcpy, memset, qsort și bsearch primesc void *.
Nu poți dereferenția direct un void *, pentru că nu se știe câți octeți să citești. Îl convertești întâi la tipul real: *(int *)p. În C (spre deosebire de C++), conversia din void * în alt pointer este implicită, deci int *v = malloc(...) nu are nevoie de cast.
Standardul nu permite aritmetică pe void *; pentru a avansa octet cu octet, convertește la unsigned char *.
Exemple
Exemplul 1: Același pointer, tipuri diferite
Funcția primește un void * și un cod care spune ce tip este, apoi face conversia potrivită.
#include <stdio.h>
enum tip { INT, DOUBLE, TEXT };
void afiseaza(const void *p, enum tip t) {
switch (t) {
case INT: printf("int: %d\n", *(const int *)p); break;
case DOUBLE: printf("double: %.2f\n", *(const double *)p); break;
case TEXT: printf("text: %s\n", (const char *)p); break;
}
}
int main(void) {
int varsta = 34;
double pret = 49.99;
afiseaza(&varsta, INT);
afiseaza(&pret, DOUBLE);
afiseaza("Iași", TEXT);
return 0;
}
Rezultat
int: 34
double: 49.99
text: Iași
Exemplul 2: Octeții unei variabile
Convertind la const unsigned char * poți citi fiecare octet. Pe procesoarele little-endian (x86, ARM din Mac-uri și telefoane), octetul cel mai puțin semnificativ vine primul.
#include <stdio.h>
#include <stdint.h>
void octeti(const void *p, size_t n) {
const unsigned char *b = p;
for (size_t i = 0; i < n; i++) printf("%02x ", b[i]);
printf("\n");
}
int main(void) {
uint32_t cod = 0x11223344;
octeti(&cod, sizeof cod);
return 0;
}
Rezultat
44 33 22 11
Exemplul 3: O funcție generică de interschimbare
Funcția nu știe tipul, doar dimensiunea. Copiază octeții cu memcpy printr-un tampon local, deci merge pentru int, double sau struct.
#include <stdio.h>
#include <string.h>
void schimba(void *a, void *b, size_t dim) {
unsigned char temp[64];
if (dim > sizeof temp) return;
memcpy(temp, a, dim);
memcpy(a, b, dim);
memcpy(b, temp, dim);
}
int main(void) {
double x = 1.5, y = 9.25;
schimba(&x, &y, sizeof x);
printf("x = %.2f, y = %.2f\n", x, y);
return 0;
}
Rezultat
x = 9.25, y = 1.50
Greșeli frecvente
- Dereferențiezi direct un
void *(*p). Compilatorul refuză; convertește întâi la tipul corect. - Convertești la un tip greșit: un
doublecitit prin(int *)dă valori fără sens și încalcă regulile de aliasing. Ține evidența tipului real. - Scrii
p + 1pe unvoid *. Clang și GCC îl acceptă ca extensie (pas de 1 octet), dar-Wpedanticavertizează; foloseșteunsigned char *.
Comenzi înrudite
Pointeri (& și *): O variabilă care păstrează adresa altei variabile; cu * citești sau modifici valoarea de la acea adresă.malloc: Cere sistemului un bloc de memorie de mărimea dată, în timpul rulării, și returnează adresa lui sau NULL dacă nu reușește.memcpy: Copiază n octeți dintr-o zonă de memorie în alta; zonele nu au voie să se suprapună.qsort: Sortează un tablou de orice tip, folosind o funcție de comparare scrisă de tine.Aritmetica pointerilor: Adunarea și scăderea pe pointeri se fac în elemente, nu în octeți: p + 1 arată spre următorul element din tablou.