struct agrupar dades relacionades
Declaració i ús
struct Persona {
char nom[20];
int edat;
};
struct Persona p1 = {"Jordi", 16};
printf("%s té %d anys\n", p1.nom, p1.edat);
El bloc struct Persona { ... } no crea cap variable: defineix una plantilla. p1 és una variable d'aquest nou tipus, i s'hi accedeix amb el punt: p1.nom, p1.edat.
Una estructura com a paràmetre
Una struct es passa per valor, igual que un int: la funció en rep una còpia completa.
void aniversari(struct Persona p) {
p.edat = p.edat + 1;
printf("Dins: %s té %d anys\n", p.nom, p.edat);
}
Com que p és una còpia, incrementar p.edat dins la funció no toca la variable original — exactament el mateix comportament que vas veure amb incrementa(int n) al capítol de funcions. Ho veuràs en detall a l'execució pas a pas.
typedef estalviar-se escriure "struct" sempre
typedef struct {
char nom[20];
int edat;
} Persona;
Persona p1 = {"Jordi", 16}; // ja no cal escriure "struct Persona"
typedef crea un àlies per a un tipus. És purament una comoditat d'escriptura: el comportament és idèntic.
union compartir el mateix espai de memòria
Una union s'escriu igual que una struct, però tots els seus membres comparteixen la mateixa adreça: només n'hi pot haver un de vàlid a la vegada.
union Dada {
int enter;
float decimal;
};
union Dada d;
d.enter = 5;
printf("%d\n", d.enter); // 5
d.decimal = 3.14;
printf("%.2f\n", d.decimal); // 3.14
// a partir d'aquí, d.enter ja no té cap sentit: s'ha sobreescrit
Una struct Dada amb els mateixos membres ocuparia l'espai d'un int més el d'un float; una union Dada només ocupa l'espai del més gran dels dos, perquè es reparteixen el mateix lloc. S'utilitzen quan saps que només et cal un valor o l'altre, mai els dos alhora.
Memòria dinàmica
Fins ara, la mida de qualsevol vector calia saber-la en compilar (#define MIDA 10). Amb malloc pots reservar memòria en temps d'execució, amb la mida que calgui en aquell moment.
Exemple
#include <stdlib.h>
int *v;
int n = 5;
v = malloc(n * sizeof(int));
if (v == NULL) {
printf("Error reservant memòria\n");
return 1;
}
// ... fer servir v[0], v[1], ... com un vector normal ...
free(v);
malloc(bytes) reserva aquest nombre de bytes i retorna un apuntador al principi; sizeof(int) et diu quants bytes ocupa un int en aquesta màquina, perquè mai cal donar-ho per fet. Si no hi ha prou memòria lliure, malloc retorna NULL — per això cal comprovar-ho sempre.
Molt important: tota memòria reservada amb malloc s'ha d'alliberar amb free quan ja no es fa servir. Oblidar-ho és una fuita de memòria (memory leak): el programa va consumint cada cop més memòria sense raó.
bonus Apuntadors a funció
Igual que pots tenir un apuntador a un int, pots tenir-ne un a una funció — útil per passar "quin codi executar" com si fos una dada més.
#include <stdlib.h>
int compara(const void *a, const void *b) {
return *(int *)a - *(int *)b;
}
int v[5] = {5, 2, 4, 1, 3};
qsort(v, 5, sizeof(int), compara);
// v ara és {1, 2, 3, 4, 5}
qsort (de <stdlib.h>) ja porta un algorisme d'ordenació fet — l'únic que li falta saber és com comparar dos elements, i per això li passes compara com a argument: una funció que rep dos apuntadors genèrics i diu quin dels dos és més petit.
Mira les estructures i la memòria dinàmica pas a pas
A la segona variant, fixa't en el contrast amb incrementa(int n): una estructura també es copia en passar-la a una funció.
Estructures
Memòria dinàmica
Codi
Variables
| Nom | Valor |
|---|
Vector (quan n'hi hagi)
Terminal
Problemes per resoldre
Sense solució ni comprovació automàtica: escriu el programa, compila'l (gcc programa.c -o programa) i prova'l amb els exemples. Les pistes són plegades perquè les obris només si les necessites.
1. Vector d'estructures
Defineix struct Llibre { char titol[30]; double preu; }; i declara un vector de 3 llibres amb valors escrits al codi. Mostra quin és el més car.
El més car és: El Petit Príncep (18.50)
Pistes
struct Llibre llibres[3] = { {"...", 12.0}, {"...", 18.5}, {"...", 9.0} };
Recorre el vector amb un bucle, igual que faries amb un vector d'int, comparant llibres[i].preu amb el candidat actual.
2. Agenda amb memòria dinàmica
Demana quantes persones vols desar, reserva amb malloc un vector d'aquesta mida de struct Persona (nom i edat), demana'n les dades i mostra la mitjana d'edat. No t'oblidis de free al final.
Quantes persones? 2 Nom 1: Jordi Edat 1: 16 Nom 2: Anna Edat 2: 20 Mitjana d'edat: 18.00
Pistes
struct Persona *agenda = malloc(n * sizeof(struct Persona)); — igual que amb un vector d'int, només que ara cada "cella" és una estructura sencera.
Pots accedir a agenda[i].nom i agenda[i].edat exactament com si agenda fos un vector normal de structs.
3. Comprovar una unió
Declara union Dada { int enter; float decimal; };. Assigna primer un enter i mostra'l; després assigna-hi un decimal i mostra'l. Finalment, torna a mostrar el membre enter i comenta (en un comentari al codi) per què el resultat ja no té sentit.
Com a enter: 7 Com a decimal: 3.14 Com a enter (ja no és vàlid): -1057904406
Pistes
No cal fer res de especial perquè surti un valor "estrany" al final: simplement torna a llegir d.enter després d'haver escrit a d.decimal. El número exacte que surti no importa — l'important és veure que ja no és el 7 que hi vas posar.
Exercicis d'autoavaluació
Aquests es generen amb valors nous a l'atzar i tenen solució comprovable.
Quina serà la sortida per terminal d'aquest programa?