C

C

Tutorial C: Funciones

C: Aprende sobre prototipos y definición de funciones en C. Mejora la gestión del código y evita inconsistencias con este tutorial detallado.

Aprende C y certifícate

Prototipos y definición de funciones

Un prototipo de función especifica su nombre, tipo de retorno y parámetros, permitiendo al compilador conocer de antemano la firma de la función antes de que se encuentre su implementación en el código. Este enfoque evita advertencias sobre declaraciones implícitas y facilita la organización de archivos fuente cuando se trabaja en proyectos con múltiples módulos.

La definición de la función es el bloque de código que realmente ejecuta las instrucciones. Incluye el mismo tipo de retorno y los tipos de los parámetros declarados en el prototipo. En esta sección se ubican las sentencias que procesan los datos y devuelven un resultado. Mantener el prototipo igual a la definición garantiza la correcta vinculación durante la fase de enlazado.

Es habitual colocar los prototipos al inicio del fichero o en un archivo de encabezado. El compilador procesa primero estas declaraciones, asegurando que cuando el programa invoque la función, ya exista información sobre su firma y su tipo de retorno.

A continuación, se muestra un ejemplo sencillo que ilustra el uso de un prototipo y su definición:

#include <stdio.h>

/* Prototipo */
int sumar(int x, int y);

int main(void) {
    int resultado = sumar(10, 5);
    printf("La suma es: %d\n", resultado);
    return 0;
}

/* Definición */
int sumar(int x, int y) {
    return x + y;
}

En este ejemplo, el prototipo de la función sumar declara que la función recibe dos enteros y devuelve un entero. Posteriormente, la definición desarrolla el algoritmo que realiza la suma y devuelve el resultado. Usar prototipos claros evita inconsistencias entre llamada y definición, especialmente en proyectos de gran tamaño.

Paso de argumentos por valor

En C, todos los argumentos se transmiten a una función mediante paso de valores, lo que implica que la función recibe una copia de cada parámetro. Dichas copias se almacenan en áreas de memoria independientes dentro de la función, de modo que cualquier modificación de los argumentos no altera el contenido original en el programa que llamó a la función.

Un aspecto significativo de este mecanismo es que se evita la alteración accidental de variables externas. El compilador reserva espacio para los parámetros en la pila (stack) en el momento de la llamada y, al finalizar la función, esa memoria local se libera. Dicho flujo es especialmente ventajoso en situaciones donde se busca aislar comportamientos y proteger datos críticos.

A continuación, se muestra un ejemplo que ilustra el paso de argumentos por valor:

#include <stdio.h>

void incrementar(int numero) {
    numero++;
    printf("Dentro de la función: %d\n", numero);
}

int main(void) {
    int valor = 5;
    incrementar(valor);
    printf("Fuera de la función: %d\n", valor);
    return 0;
}

En este ejemplo, la función incrementar opera sobre una copia de la variable valor. Tras llamar a incrementar, el valor original permanece inalterado en el ámbito de main, demostrando así que las modificaciones efectuadas en la función no repercuten fuera de ella. Esta característica de C permite que cada función trabaje sobre datos temporales, manteniendo la coherencia y reduciendo efectos colaterales.

Tipos de retorno y ámbito (scope) de variables

Las funciones en C pueden devolver diferentes tipos de datos, como int, float o incluso void cuando no se requiere un resultado. El tipo de retorno debe declararse al inicio de la función, indicando claramente qué valor se devuelve. En caso de que la función retorne un objeto de un tipo compuesto, suele ser recomendable emplear estructuras o, si es necesario, punteros.

Para controlar el ámbito de una variable, es clave entender que en C existen diferentes niveles de visibilidad. Una variable declarada dentro de una función pertenece al ámbito local de dicha función; esto significa que solo puede usarse durante la ejecución de esa función y deja de existir al salir de ella. Por el contrario, una variable global, declarada fuera de cualquier función, puede emplearse en todo el fichero, y si se utiliza la palabra clave extern en otros ficheros, su alcance se extiende a distintos módulos.

El almacenamiento de las variables locales se reserva en la pila, mientras que las variables globales suelen residir en la memoria estática. Cuando una variable se declara con la palabra clave static dentro de una función, mantiene su valor entre llamadas sucesivas, aunque continúe siendo inaccesible desde fuera de la función. Este comportamiento se aprovecha en situaciones donde es útil conservar el estado interno sin exponerlo de manera global.

En el siguiente ejemplo, se ilustran distintos tipos de retorno y ámbitos de variables:

#include <stdio.h>

/* Variable global */
static int contadorGlobal = 0;

/* Función que retorna un valor entero */
int incrementarGlobal(void) {
    contadorGlobal++;
    return contadorGlobal;
}

/* Función sin retorno que utiliza ámbito local */
void imprimirLocal(void) {
    int valorLocal = 100;
    printf("Valor local dentro de la función: %d\n", valorLocal);
}

int main(void) {
    printf("Contador global: %d\n", incrementarGlobal());
    imprimirLocal();
    return 0;
}

En este código, se observa que contadorGlobal conserva su valor a lo largo de múltiples llamadas debido a su almacenamiento estático global, mientras que valorLocal solo existe dentro de la función imprimirLocal y se destruye al finalizar dicha función. El retorno de la función incrementarGlobal es de tipo int, por lo que se especifica con claridad el valor devuelto en la instrucción return.

Recursividad

La recursividad en C consiste en que una función se invoque a sí misma, ya sea de forma directa (llamándose de manera explícita) o indirecta (mediante otra función intermedia). Este mecanismo se basa en resolver un problema dividiéndolo en subproblemas de la misma naturaleza, lo cual puede simplificar la lógica en ciertos casos. Sin embargo, cada llamada recursiva consume espacio en la pila y, si las llamadas son muy profundas o no se definen condiciones base correctamente, se puede agotar la memoria disponible y producir errores en tiempo de ejecución.

Para implementar la recursividad, es esencial establecer un caso base que detenga la cadena de llamadas. Sin una condición clara de terminación, la función provocaría llamadas infinitas. Por otro lado, cada llamada reduce la complejidad del problema, avanzando paso a paso hacia el caso base y retornando los resultados parciales hasta reconstruir la solución completa.

A modo de ejemplo, se muestra una implementación recursiva del cálculo de la serie de Fibonacci, donde cada término depende de los dos anteriores:

#include <stdio.h>

int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main(void) {
    int resultado = fibonacci(6);
    printf("El término en la posición 6 es: %d\n", resultado);
    return 0;
}

En este ejemplo, la función fibonacci llama de nuevo a fibonacci con valores decrecientes hasta llegar a los casos n <= 1, retornando así la sucesión. Cada llamada se apila en la memoria hasta que se alcanza el caso base, momento en el que se van resolviendo las sumas pendientes.

La recursividad también puede darse de manera indirecta, donde la llamada recursiva no se produce de forma inmediata, sino a través de otra función que eventualmente retorna el control a la función inicial. Aunque menos frecuente, este patrón puede darse en algoritmos complejos que involucran pasos intermedios. En cualquier caso, es recomendable analizar la eficiencia y considerar alternativas no recursivas, sobre todo cuando el tamaño del problema pueda comprometer el espacio en la pila.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

Plan mensual

19.00 € /mes

Precio normal mensual: 19 €
47 % DE DESCUENTO

Plan anual

10.00 € /mes

Ahorras 108 € al año
Precio normal anual: 120 €
Aprende C online

Todas las lecciones de C

Accede a todas las lecciones de C y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a C y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la importancia de los prototipos en C.
  • Diferenciar entre prototipos y definiciones de funciones.
  • Entender el concepto de paso de argumentos por valor.
  • Identificar diferentes tipos de retorno.
  • Aprender sobre el ámbito de variables dentro y fuera de funciones.
  • Aplicar recursividad en funciones, entendiendo su uso y limitaciones.