Per què funcions?
Una funció és un bloc de codi amb nom, que pots cridar des de diversos llocs del programa sense repetir-lo. Ajuda a organitzar el codi en trossos petits, cadascun amb una sola responsabilitat.
Definició i crida
Sintaxi
tipus_de_retorn nom_funcio(paràmetres) {
// cos de la funció
}
Exemple
void saluda() {
printf("Hola des de la funció!\n");
}
int main() {
saluda();
return 0;
}
Definir una funció no l'executa: el seu cos només corre quan algú la crida, escrivint el seu nom seguit de parèntesis. Aquí saluda() no rep cap paràmetre ni retorna cap valor (per això el tipus és void).
Paràmetres: sempre per valor
En C, quan passes una variable a una funció, la funció en rep una còpia. Qualsevol canvi que faci la funció sobre el seu paràmetre no es veu des de fora.
Exemple
void incrementa(int n) {
n = n + 1;
printf("Dins de la funció: %d\n", n);
}
int main() {
int x = 5;
incrementa(x);
printf("Fora de la funció: %d\n", x);
return 0;
}
La sortida és Dins de la funció: 6 i després Fora de la funció: 5. incrementa rep una còpia de x anomenada n; modificar n no toca x per a res. Aquesta és exactament la raó per la qual una funció no pot, només amb paràmetres normals, intercanviar els valors de dues variables del cridant — això necessita apuntadors, que veurem més endavant.
return i void
Una funció pot retornar un valor amb return, o no retornar-ne cap (void). return també talla l'execució de la funció a l'instant, encara que hi hagi més codi després.
Exemple
int quadrat(int n) {
return n * n;
}
int main() {
int resultat;
resultat = quadrat(6);
printf("El quadrat de 6 és %d\n", resultat);
return 0;
}
El tipus abans del nom de la funció (int) ha de coincidir amb el tipus del que retornes. Una funció void pot fer servir return; sense cap valor per sortir-ne abans d'hora, però mai return algun_valor;.
Prototips
Si vols cridar una funció abans de definir-la (per exemple, des de main(), que sol anar primer), cal avisar el compilador amb un prototip: la mateixa capçalera, acabada en punt i coma.
#include <stdio.h>
int suma(int a, int b); // prototip
int main() {
int resultat = suma(3, 4);
printf("%d\n", resultat);
return 0;
}
int suma(int a, int b) { // definició completa, aquí sota
return a + b;
}
Sense el prototip, el compilador es trobaria amb suma(3, 4) sense saber encara que aquesta funció existirà més avall, i donaria error.
Funcions que en criden d'altres
Una funció pot cridar qualsevol altra funció ja definida (o amb prototip), incloent-hi funcions que tu mateix has escrit.
int quadrat(int n) {
return n * n;
}
int cub(int n) {
return quadrat(n) * n;
}
cub no recalcula n * n: reaprofita quadrat, que ja ho fa. Quan una funció en crida una altra, l'execució "puja" a la nova funció i, quan aquesta acaba, torna exactament al punt on l'havies cridat.
Aleatorietat
Les funcions rand() i srand() (de <stdlib.h>) generen nombres pseudoaleatoris.
Exemple: simular un dau
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
int dau;
srand(time(NULL));
dau = rand() % 6 + 1;
printf("Has tret un %d\n", dau);
return 0;
}
srand(time(NULL)) "llavor" el generador amb l'hora actual, perquè cada execució doni una seqüència diferent (sense això, rand() repetiria sempre la mateixa). rand() sol retorna un enter gran; % 6 el redueix a 0-5, i + 1 el desplaça a 1-6.
En general, per obtenir un valor entre a i b (tots dos inclosos): rand() % (b - a + 1) + a.
Compte: rand() no dona els mateixos valors a tot arreu
rand() depèn de la implementació: a Linux (glibc) RAND_MAX val 2147483647, i a MinGW (Windows) només 32767, amb seqüències diferents. Un programa amb aleatorietat és, per definició, imprevisible en el seu resultat exacte — més endavant, als exercicis, només et demanarem comprovar propietats (per exemple, que el valor surti sempre entre 1 i 6), mai un número concret.
Mira el programa executar-se pas a pas
Fixa't sobretot en com l'execució "salta" a la funció quan es crida, i "torna" exactament al punt de la crida quan s'acaba.
Funcions
Aleatorietat
Codi
Variables
| Nom | Valor |
|---|
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. Totes les funcions reben i retornen només valors simples (int, double...), mai vectors. Les pistes són plegades perquè les obris només si les necessites.
1. És parell?
Escriu una funció es_parell(int n) que retorni 1 si n és parell i 0 si és senar. Des de main(), demana un enter i mostra si és parell o senar fent servir aquesta funció.
Nombre: 7
És senar
Pistes
El cos pot ser una sola línia: return n % 2 == 0; — una comparació ja val 0 o 1.
A main(), crida la funció dins la condició d'un if: if (es_parell(n)) { ... }.
2. Any de traspàs
Escriu una funció es_traspas(int any) que retorni 1 si l'any és de traspàs i 0 si no ho és (divisible entre 4 però no entre 100, o bé divisible entre 400). Prova-la amb diversos anys des de main().
Any: 2024 És de traspàs Any: 1900 No és de traspàs
Pistes
És exactament la mateixa condició que potser ja vas escriure al capítol de condicionals — ara la poses dins d'una funció que la retorna.
return (any % 4 == 0 && any % 100 != 0) || any % 400 == 0;
3. Àrea d'un cercle
Escriu una funció area_cercle(double radi) que retorni l'àrea d'un cercle d'aquest radi (àrea = π · radi²). Demana el radi des de main() i mostra el resultat amb 2 decimals.
Radi: 3
Àrea: 28.27
Pistes
Pots definir #define PI 3.14159265 a dalt de l'arxiu, abans de main(), i fer-lo servir dins la funció.
El tipus de retorn ha de ser double, no int, o perdràs tots els decimals.
4. Compta sisos
Escriu una funció tira_dau() que retorni un valor a l'atzar entre 1 i 6. Des de main(), tira el dau 20 vegades i compta (amb una variable comptador, sense desar res més) quantes vegades ha sortit un 6.
Sisos en 20 tirades: 4
Pistes
Fes srand(time(NULL)); un sol cop, a main(), abans del bucle — mai dins de tira_dau(), o sempre tornaries a començar la mateixa seqüència.
Només et cal una variable comptador que comenci a 0 i s'incrementi cada cop que tira_dau() == 6. Com que el resultat és aleatori, no hi ha un número "correcte": qualsevol valor entre 0 i 20 és un resultat vàlid.
Exercicis d'autoavaluació
Aquests sí que es generen amb valors nous a l'atzar i tenen solució comprovable (no fan servir rand(), perquè aquest mai dona un valor fix per comprovar).
Quina serà la sortida per terminal d'aquest programa?