// funcions i aleatorietat

Funcions en C

Definir i cridar funcions que reben i retornen valors simples, i generar nombres a l'atzar amb rand() — els vectors queden per a un capítol propi, més endavant.

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

NomValor

Terminal

Pas 1 / 1

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?