Fundamentos

Funciones en C++: la imagen completa

Respuesta corta: una función es un bloque con nombre, con entradas tipadas (parámetros) y una salida tipada (el valor de retorno). Llamarla ejecuta el bloque con unas entradas concretas y devuelve el resultado.

Anatomía

int sumar(int a, int b) {   // tipo de retorno, nombre, lista de parámetros
    return a + b;           // cuerpo, termina devolviendo un int
}

Hay cuatro cosas que leer: int es lo que vuelve, sumar es el nombre, int a, int b son las entradas y return produce la salida. Una función sin valor de retorno se declara void.

Llamar es sustituir

#include <iostream>
using namespace std;

int sumar(int a, int b) {
    return a + b;
}

int cuadrado(int x) {
    return x * x;
}

int main() {
    cout << sumar(2, 3) << endl;         // 5
    cout << cuadrado(sumar(1, 1)) << endl; // (2)^2 = 4 — una llamada dentro de otra
    return 0;
}

La segunda línea es la clave: una llamada a función es un valor, así que puede ir donde iría un literal. Eso es lo que hace componibles a las funciones.

Los parámetros son copias

Este es el hecho más importante sobre las funciones en C++:

#include <iostream>
using namespace std;

void intentarCambiar(int n) {
    n = 999;          // cambia solo la copia local
}

int main() {
    int x = 5;
    intentarCambiar(x);
    cout << x << endl;   // sigue siendo 5
    return 0;
}

Para cambiar de verdad la variable del llamador hay que pasar una dirección (puntero) o una referencia. La comparación completa, con diagramas, está en paso por valor, puntero o referencia.

Devolver frente a imprimir

Una función que devuelve se puede usar en expresiones; una que imprime, no:

#include <iostream>
using namespace std;

int doble(int n) { return n * 2; }

void imprime(int n) { cout << n * 2 << endl; }

int main() {
    int total = doble(10) + 1;   // funciona: doble() es un valor
    cout << total << endl;
    imprime(10);                 // funciona, pero no se puede reutilizar
    return 0;
}

Regla de la casa: calcula en las funciones (return), imprime en main. Mantiene la función testeable y reutilizable.

Ámbito: dónde viven los nombres

#include <iostream>
using namespace std;

int contador = 0;        // global: visible en todas partes

int bump() {
    int local = 5;       // local: existe solo durante esta llamada
    contador++;
    return contador + local;
}

int main() {
    cout << bump() << endl;   // 6
    cout << bump() << endl;   // 7 — el global sobrevivió
    // cout << local;         // error: local no está en este ámbito
    return 0;
}

Prefiere variables locales: pasa como parámetro lo que la función necesite y devuelve lo que produzca. Los globales son cómodos y hacen el código difícil de razonar en silencio.

Para qué dividir el código

Los tres bugs

Practica: ¿Es primo? y MCD quedan más limpios con una función auxiliar. Lee después: recursión y luego formas de pasar.