HOWTO · C
for-each bucle en C
El siguiente contenido investigará si el lenguaje informático C admite o no un bucle foreach.
En esta página
C no incluye un bucle for-each que recorra automáticamente todos los elementos de una colección. Puedes lograr un estilo de iteración similar con un bucle for o while normal, pero el código debe saber dónde termina la colección. Para un array, conserva la cantidad de elementos; para una lista enlazada, sigue el puntero next de cada nodo hasta que sea nulo.
¿C tiene un bucle for-each integrado?
No. El lenguaje C no ofrece una construcción foreach específica como las de Python o JavaScript. Aun así, es sencillo escribir un bucle al estilo for-each cuando la colección y sus límites están claros. Para un array, una opción habitual es recorrerlo mediante índices. Para una lista enlazada, avanza un puntero de un nodo al siguiente.
La diferencia clave frente a muchos lenguajes de nivel superior es que C no asocia una longitud a un puntero a un array. Cuando una función necesita procesar un array, pasa la longitud junto con el puntero. Así, la condición de parada queda explícita y se evita leer más allá de los elementos de la colección.
Recorrer un array con un bucle for
Si el array se declaró en el mismo ámbito, calcula la cantidad de elementos con sizeof array / sizeof array[0]. Después, usa un índice desde cero hasta esa cantidad, sin incluirla.
El siguiente programa autónomo imprime cada elemento en orden. La expresión que calcula la longitud es válida aquí porque numbers todavía es un array dentro de main.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
#include <stddef.h>
#include <stdio.h>
int main(void) {
int numbers[] = {10, 20, 30, 40, 50};
size_t length = sizeof numbers / sizeof numbers[0];
for (size_t i = 0; i < length; ++i) {
printf("%d\n", numbers[i]);
}
return 0;
}
10
20
30
40
50
Cuando se pasa un array a una función, el parámetro es un puntero al primer elemento; no lleva consigo el tamaño del array. Por eso, aplicar sizeof a ese parámetro devuelve el tamaño del puntero, no el tamaño en bytes del array original. Pasa por separado la cantidad de elementos, por ejemplo, como size_t length, y recorre el array mientras i < length. Quien llama a la función puede calcular la cantidad antes de pasar el array. Si el array se asignó dinámicamente, conserva la cantidad obtenida durante la asignación o almacenada en la estructura de datos que lo administra.
Usar un bucle while o recorrer una lista enlazada
Un bucle while funciona igual de bien cuando su condición expresa de forma natural si queda otro elemento. Para un array, inicializa un índice en cero, continúa mientras sea menor que la longitud guardada e increméntalo una vez por iteración. Al igual que con un bucle for, es importante respetar el límite.
En una lista enlazada, la condición de parada es distinta: continúa mientras el puntero al nodo actual no sea nulo, procesa ese nodo y luego avanza a current->next. Por ejemplo, puedes declarar un nodo como struct Node { int value; struct Node *next; };; como current es un puntero a un nodo, accede a sus campos con ->. La lista termina cuando current pasa a ser NULL. No uses la fórmula con sizeof de un array para una lista enlazada: sus nodos son objetos independientes conectados mediante punteros, y la lista no tiene una longitud de array. Si necesitas contar sus nodos, recórrela o mantén la cantidad en la estructura que la administra.
¿Cuándo conviene agrupar la iteración en una función o macro?
Una función resulta útil cuando varias partes del programa realizan la misma operación con cada elemento de un array. Puede recibir un puntero a los elementos, una cantidad de tipo size_t y la operación que debe aplicar. El parámetro de una función de devolución de llamada podría tener el tipo void (*visit)(int), y la función podría llamar a visit(array[i]) para cada índice válido. Pasa la cantidad explícitamente: dentro de la función, el parámetro del array es un puntero, así que no se puede recuperar el tamaño original con sizeof.
Una función que acepta una devolución de llamada también permite aplicar distintas operaciones, como imprimir o acumular valores. Asegúrate de que el tipo de la devolución de llamada corresponda al tipo de los elementos y decide si necesita contexto adicional; en C, las devoluciones de llamada pueden recibirlo mediante un parámetro void *.
Una macro puede abreviar un bucle, pero realiza sustituciones de texto y puede evaluar sus argumentos más de una vez. También puede ocultar los límites y los tipos que hacen segura la iteración. Si usas una, comprueba que el límite corresponda al array y evita pasar expresiones con efectos secundarios. Para reutilizar el código, una función con una longitud explícita suele ser más fácil de revisar, depurar y mantener.