Funciones en C — teoría con prototipos, void y paso por valor y por referencia cobrando el billete de la guagua (y el saldo que no bajaba)
Mi primera función «de verdad» en C cobraba un viaje del bono de la guagua. La llamé dos veces, imprimí el saldo y seguía en 10 euros exactos. Repasé la resta, el printf y hasta el precio del billete antes de entender lo que pasaba: la función había cobrado los viajes a una copia del saldo. En C, salvo que tú digas lo contrario, cada función trabaja con copias.
Este artículo va de eso y de todo lo que lo rodea: cómo se escribe una función en C, qué significa exactamente void (la duda que más se busca del tema), por qué existe el prototipo y por qué gcc 14 ya no te perdona olvidarlo, cómo se devuelve un resultado, qué es la pila de llamadas y, sobre todo, la diferencia entre paso por valor y paso por referencia, que es donde se juntan las funciones con lo que ya sabes de punteros en C. El ejemplo guía sigue siendo la guagua: billetes por zonas, el saldo del bono en céntimos y los viajeros de cada parada. Todas las salidas son de una ejecución real con gcc 14.
Tabla de Contenidos
Qué es una función en C
Una función es un bloque de código con nombre que hace una tarea concreta y se puede usar desde cualquier parte del programa. main ya es una función: la que el sistema llama al arrancar. Si vienes de Funciones en Python, la idea es la misma:
def precio_billete(zonas):
return 155 + (zonas - 1) * 20
En C, la misma función necesita decir el tipo de lo que devuelve y el de cada parámetro:
#include <stdio.h>
/* Devuelve el precio del billete en céntimos según las zonas */
int precio_billete(int zonas) {
return 155 + (zonas - 1) * 20; /* 1,55 euros y 20 céntimos por zona extra */
}
int main(void) {
int precio = precio_billete(3); /* LLAMADA: 3 entra como zonas */
printf("Billete de 3 zonas: %d céntimos\n", precio);
printf("Billete de 1 zona: %d céntimos\n", precio_billete(1));
return 0;
}
Salida:
Billete de 3 zonas: 195 céntimos Billete de 1 zona: 155 céntimos
Las piezas de una función, de izquierda a derecha:
| Pieza | En el ejemplo | Para qué sirve |
|---|---|---|
| Tipo de retorno | int | qué tipo de valor devuelve |
| Nombre | precio_billete | cómo se la llama |
| Parámetros | (int zonas) | qué recibe, cada uno con su tipo |
| Cuerpo | { return …; } | lo que hace |
Cuando main escribe precio_billete(3), el 3 se copia en el parámetro zonas, la función calcula 155 + 2 × 20 = 195 y el return devuelve ese número al sitio de la llamada, donde se guarda en precio. Con 1 zona, 155 + 0 = 155.
En Python los tipos son opcionales; en C son obligatorios. Si una función devuelve int y escribes return 1.95;, C convierte el resultado a 1 sin preguntar. Por eso el precio va en céntimos: con enteros no hay decimales que perder.
Qué es void en C: el tipo que más confunde
void significa «nada». Es la forma de C de decir que un hueco está vacío, y aparece en dos sitios distintos con dos significados distintos:
- Como tipo de retorno: la función hace algo, pero no devuelve ningún valor. No lleva
return valor;(como mucho unreturn;sin nada para salir antes). - Como lista de parámetros: la función no recibe nada.
int primera_salida(void)se llama así:primera_salida().
Las dos juntas en un programa:
#include <stdio.h>
/* void como tipo de retorno: hace algo y no devuelve nada */
void separador(int largo) {
for (int i = 0; i < largo; i++)
printf("-");
printf("\n");
}
/* void en los parámetros: no recibe nada */
int primera_salida(void) {
return 610; /* 6:10, la primera guagua */
}
/* void en las dos partes */
void cabecera(void) {
separador(26);
printf("LÍNEA 201 San Telmo-Teror\n");
separador(26);
}
int main(void) {
cabecera();
printf("Primera salida: %d\n", primera_salida());
return 0;
}
Salida:
-------------------------- LÍNEA 201 San Telmo-Teror -------------------------- Primera salida: 610
separador dibuja la línea y no devuelve nada (void delante); primera_salida no recibe nada (void entre paréntesis) y devuelve 610; cabecera ni recibe ni devuelve: solo llama a otras dos funciones. Fíjate en que una función void no se puede usar como valor: int x = separador(26); no compila, porque no hay nada que guardar.
¿Y los paréntesis vacíos, int primera_salida()? En C11, que es el estándar de IC2, unos paréntesis vacíos en una definición significan «no digo qué parámetros tiene», no «no tiene parámetros». El compilador no comprobará que la llames sin argumentos. Con (void) sí lo comprueba. Desde C23 las dos formas significan lo mismo, pero en la asignatura se compila con -std=c11: mejor (void).
Lee la declaración de izquierda a derecha: void cabecera(void) es «no devuelve nada, se llama cabecera, no recibe nada». Si delante hay un tipo distinto de void, la función tiene que acabar con un return de ese tipo.
void main: lo que verás en los apuntes
En los apuntes de IC2 muchos ejemplos empiezan por void main(). ¿Está bien? gcc lo compila, pero avisa:
#include <stdio.h>
void main(void) { /* como en los apuntes */
printf("La guagua sale a las 6:10\n");
}
void_main.c:3: warning: return type of 'main' is not 'int' [-Wmain]
La guagua sale a las 6:10
El estándar de C dice que main devuelve int: ese número es el código de salida que recibe la terminal (el echo $? de Linux), y 0 significa «todo bien». Con void main no devuelves nada y el código de salida queda al azar: en mi ejecución el programa terminó con 26 (casi seguro, el valor que dejó printf al escribir sus 26 caracteres). Un script que compruebe si el programa ha ido bien lo daría por fallido. En tus programas, siempre int main(void) y return 0;.
Cómo declarar y llamar una función correctamente
Una función tiene tres momentos: la definición (la firma con su cuerpo), el prototipo (solo la firma, terminada en punto y coma) y la llamada (usarla). El orden en que aparecen en el archivo importa, porque el compilador lee el programa de arriba abajo una sola vez.
Mira qué pasa si main llama a una función que está definida más abajo y no hay prototipo:
#include <stdio.h>
int main(void) {
printf("%d\n", precio_billete(3)); /* el compilador aún no la conoce */
return 0;
}
int precio_billete(int zonas) {
return 155 + (zonas - 1) * 20;
}
sin_prototipo.c: In function 'main': sin_prototipo.c:4: error: implicit declaration of function 'precio_billete' [-Wimplicit-function-declaration]
«implicit declaration»: cuando gcc llega a la línea 4 todavía no sabe qué es precio_billete. En compiladores antiguos esto era solo un aviso y C se inventaba que la función devolvía int; desde gcc 14 (el de Fedora) es un error y el programa no compila. Hay dos arreglos: poner la función entera antes de main, o poner su prototipo arriba.
Por qué existe el prototipo: la razón real
El prototipo le dice al compilador «esta función existe, recibe esto y devuelve aquello; el cuerpo viene después». Con eso basta para comprobar cada llamada:
#include <stdio.h>
/* PROTOTIPOS: la firma, terminada en ; */
int precio_billete(int zonas);
void imprimir_ticket(int zonas, int precio);
int main(void) {
for (int z = 1; z <= 3; z++)
imprimir_ticket(z, precio_billete(z)); /* una función dentro de otra */
return 0;
}
/* DEFINICIONES: después de main, con su cuerpo */
int precio_billete(int zonas) {
return 155 + (zonas - 1) * 20;
}
void imprimir_ticket(int zonas, int precio) {
printf("Zonas: %d | %d,%02d euros\n", zonas, precio / 100, precio % 100);
}
Salida:
Zonas: 1 | 1,55 euros Zonas: 2 | 1,75 euros Zonas: 3 | 1,95 euros
Es el estilo de los laboratorios de IC2: prototipos arriba, main en medio, definiciones al final. Así main queda a la vista y se lee como un índice del programa. Y cuando el programa crezca y lo partas en varios archivos, los prototipos son exactamente lo que va en el .h.
Dos detalles de este programa: imprimir_ticket(z, precio_billete(z)) usa el resultado de una función como argumento de otra (primero se calcula el precio y luego se imprime), y %02d rellena con un cero para que 5 céntimos salgan como 05: 175 / 100 = 1 euro y 175 % 100 = 75 céntimos.
Que el prototipo y la definición no coincidan (int precio_billete(int zonas); arriba y double precio_billete(int zonas) abajo). gcc lo detecta como «conflicting types». El prototipo es un contrato: la definición tiene que cumplirlo letra a letra.
Funciones con retorno: return
return hace dos cosas a la vez: termina la función en ese mismo instante y entrega el valor a quien la llamó. Una función puede tener varios return, pero en cada llamada solo se ejecuta uno:
#include <stdio.h>
double media(int a, int b, int c) {
return (a + b + c) / 3.0; /* 3.0 hace que la división sea decimal */
}
int maximo(int a, int b) {
if (a > b)
return a; /* sale aquí si a es mayor */
return b; /* si no, llega hasta aquí */
}
int bono_joven(int edad) {
return edad >= 14 && edad <= 30; /* 1 (verdadero) o 0 (falso) */
}
int main(void) {
printf("Media de viajeros: %.2f\n", media(12, 7, 9));
printf("Parada con más: %d\n", maximo(12, 15));
printf("¿Bono joven a los 19? %d\n", bono_joven(19));
printf("¿Bono joven a los 45? %d\n", bono_joven(45));
return 0;
}
Salida:
Media de viajeros: 9.33 Parada con más: 15 ¿Bono joven a los 19? 1 ¿Bono joven a los 45? 0
mediadivide entre3.0y no entre3: con tresint, (12 + 7 + 9) / 3 daría 9 (división entera); con3.0da 28 / 3 = 9,33.maximotiene dos salidas: sia > b, el primerreturnla termina y el segundo ni se mira. Con (12, 15), 12 > 15 es falso y llega alreturn b.bono_jovendevuelve directamente el resultado de una comparación: en C es unintque vale 1 (verdadero) o 0 (falso). Con 19 años, 19 >= 14 y 19 <= 30: 1. Con 45: 0.
Una función que no es void y en algún camino se queda sin return. gcc avisa con control reaches end of non-void function y, si ese camino se ejecuta, la función devuelve basura. Lo verás en «¿Qué falla aquí?» de los ejercicios, con un billete de 3 zonas que costaba 3 céntimos.
Funciones sin retorno: void
Cuando lo que importa es lo que la función hace (imprimir, modificar un vector) y no un valor, el tipo de retorno es void. Ya has visto separador, cabecera e imprimir_ticket. Una función void puede usar return; sin valor para terminar antes, por ejemplo al detectar un dato no válido:
void imprimir_ticket(int zonas, int precio) {
if (zonas < 1)
return; /* nada que imprimir: salgo ya */
printf("Zonas: %d | %d,%02d euros\n", zonas, precio / 100, precio % 100);
}
Y al revés: si la función tiene que avisar de si algo ha ido bien o mal, no la hagas void; que devuelva un int con 1 o 0. Es lo que hace la función cobrar un poco más abajo.
Paso por valor: la copia
Aquí está la anécdota del principio. Cuando llamas a una función, C copia cada argumento en su parámetro. La función trabaja con esa copia, y lo que haga con ella no llega a las variables de quien la llamó. Imprimiendo las direcciones se ve con claridad:
#include <stdio.h>
void cobrar_copia(int saldo, int precio) {
printf(" dentro: saldo vive en %p\n", (void *)&saldo);
saldo = saldo - precio; /* cambia la copia */
printf(" dentro: saldo = %d\n", saldo);
}
int main(void) {
int saldo = 1000;
printf("main: saldo vive en %p\n", (void *)&saldo);
cobrar_copia(saldo, 155);
printf("main: saldo = %d\n", saldo); /* sigue igual */
return 0;
}
Salida:
main: saldo vive en 0x7ffc212aef64 dentro: saldo vive en 0x7ffc212aef4c dentro: saldo = 845 main: saldo = 1000
El saldo de main vive en …ef64; el saldo de la función, en …ef4c. Son dos variables distintas que se llaman igual. Dentro, la copia baja a 845; fuera, main sigue con 1000. Es lo mismo que pasa en Python cuando le pasas un número a una función y lo cambias dentro.
El paso por valor es lo normal y es seguro: una función no puede estropear tus variables por accidente. Pero a veces es justo lo que quieres que haga.
Paso por referencia, con punteros
C no tiene paso por referencia «de verdad», como C++; tiene algo que hace lo mismo: pasar la dirección de la variable. La función recibe un puntero y, con *, escribe en la variable original. Es exactamente lo que viste en el swap de la práctica de punteros.
Esta versión de cobrar combina las dos cosas: el saldo cambia a través del puntero y el return dice si se ha podido cobrar:
#include <stdio.h>
/* Cobra si hay saldo: devuelve 1 si pudo cobrar y 0 si no.
El saldo cambia a través del puntero; el éxito sale por return. */
int cobrar(int *saldo, int precio) {
if (*saldo < precio)
return 0; /* no toco nada */
*saldo = *saldo - precio; /* cambio la variable de main */
return 1;
}
int main(void) {
int saldo = 400;
int viaje = 1;
while (cobrar(&saldo, 155)) { /* &saldo: la dirección, no el valor */
printf("Viaje %d pagado, quedan %d céntimos\n", viaje, saldo);
viaje++;
}
printf("Sin saldo para el viaje %d (quedan %d)\n", viaje, saldo);
return 0;
}
Salida:
Viaje 1 pagado, quedan 245 céntimos Viaje 2 pagado, quedan 90 céntimos Sin saldo para el viaje 3 (quedan 90)
Vuelta a vuelta: 400 − 155 = 245, 245 − 155 = 90, y con 90 céntimos ya no hay para otro viaje: cobrar devuelve 0 sin tocar el saldo, el while termina y el programa dice que el viaje 3 no se puede pagar.
| Paso por valor | Paso por referencia | |
|---|---|---|
| Parámetro | int saldo | int *saldo |
| Llamada | cobrar_copia(saldo, 155) | cobrar(&saldo, 155) |
| Dentro se usa | saldo | *saldo |
| ¿Cambia la variable de main? | No | Sí |
| Úsalo cuando | la función solo necesita el dato | la función tiene que modificarlo o devolver más de un resultado |
Olvidar el & en la llamada: cobrar(saldo, 155). Con gcc 14 es un error de compilación (pasas un int donde se espera un int *); con compiladores antiguos compilaba y la función escribía en la dirección 400, con un Segmentation fault casi seguro.
Comparativa visual: valor frente a referencia
Con las direcciones reales de la ejecución anterior, este es el dibujo que conviene tener en la cabeza:
Arriba, la copia vive en otra casilla y muere con la función; abajo, la función guarda la dirección de la casilla de main y escribe en ella. Si quieres verlo moverse, el programa de la sección de Python Tutor hace las dos llamadas seguidas.
Paso de arrays a funciones
Los vectores son el caso especial. Cuando pasas un vector a una función, C no copia las casillas: pasa la dirección de la primera (lo que ya sabes de punteros: el nombre del vector se convierte en esa dirección). Consecuencias:
- La función puede modificar el vector original sin necesidad de
&. int v[]en un parámetro es exactamente lo mismo queint *v.- La función no sabe cuántas casillas tiene el vector: hay que pasarle el tamaño aparte.
El tercer punto es una trampa clásica, y gcc la vigila:
#include <stdio.h>
/* int v[] en un parámetro es lo mismo que int *v */
int total_subidas(int v[], int n) {
printf(" sizeof v en la función = %zu\n", sizeof v);
int total = 0;
for (int i = 0; i < n; i++)
total += v[i];
return total;
}
void duplicar(int *v, int n) { /* cambia el vector de main */
for (int i = 0; i < n; i++)
v[i] = v[i] * 2;
}
int main(void) {
int subidas[5] = {12, 7, 9, 4, 15};
printf("sizeof subidas en main = %zu\n", sizeof subidas);
printf("Total: %d\n", total_subidas(subidas, 5));
duplicar(subidas, 5); /* sin &: el nombre ya es una dirección */
printf("Tras duplicar, subidas[0] = %d\n", subidas[0]);
return 0;
}
arrays.c: In function 'total_subidas': arrays.c:5: warning: 'sizeof' on array function parameter 'v' will return size of 'int *' [-Wsizeof-array-argument] arrays.c:4: note: declared here
Salida:
sizeof subidas en main = 20 sizeof v en la función = 8 Total: 47 Tras duplicar, subidas[0] = 24
En main, sizeof subidas da 20 (5 enteros de 4 bytes). Dentro de la función, sizeof v da 8: el tamaño de un puntero. Por eso el aviso dice que sizeof sobre un parámetro vector «devolverá el tamaño de int *». El total sí es correcto, 12 + 7 + 9 + 4 + 15 = 47, porque se recorre con la n que le pasamos. Y duplicar cambia el vector de main: la casilla 0 pasa de 12 a 24 sin & y sin return.
Si una función recibe un vector que no debe modificar, decláralo const int v[]. Si por error escribes v[i] = … dentro, gcc da error. Es una forma barata de dejar claro qué funciones leen y cuáles escriben.
La pila de llamadas y las variables locales
Cada vez que llamas a una función, el programa reserva para ella un marco en la pila: ahí viven sus parámetros y sus variables locales. Cuando la función hace return, el marco se libera y esas variables dejan de existir. Por eso dos funciones pueden tener una variable total cada una sin pisarse.
Con las direcciones de una variable de cada marco se ve cómo se apilan:
#include <stdio.h>
double media(int total, int n) {
double m = (double)total / n;
printf(" media: m en %p\n", (void *)&m);
return m;
}
void resumen(int *v, int n) {
int total = 0;
printf(" resumen: total en %p\n", (void *)&total);
for (int i = 0; i < n; i++)
total += v[i];
printf(" resultado: %.2f\n", media(total, n));
}
int main(void) {
int subidas[4] = {12, 7, 9, 4};
printf("main: subidas en %p\n", (void *)subidas);
resumen(subidas, 4);
return 0;
}
Salida:
main: subidas en 0x7ffe57bcaf00
resumen: total en 0x7ffe57bcaee0
media: m en 0x7ffe57bcaeb0
resultado: 8.00
main llama a resumen y resumen llama a media: tres marcos vivos a la vez. Las direcciones bajan (…af00, …aee0, …aeb0): en Linux la pila crece hacia direcciones más pequeñas. La media de 12, 7, 9 y 4 es 32 / 4 = 8,00.
Esto explica dos errores que verás pronto: devolver la dirección de una variable local (su marco ya no existe cuando la usas, como en los ejercicios de punteros) y una recursión sin fin, que apila marcos hasta que la pila se acaba.
Variables globales: las de los apuntes
Una variable declarada fuera de todas las funciones es global: la ven y la modifican todas. Los apuntes de IC2 tienen un ejemplo con dos funciones, F1 y F2, que suman y restan sobre una variable global R. Este es el mismo programa con la guagua:
#include <stdio.h>
#define N 5 /* paradas en las que sube un viajero */
#define M 2 /* paradas en las que bajan dos */
int dentro = 0; /* GLOBAL: la ven todas las funciones */
void suben(void) {
for (int i = 0; i < N; i++) {
dentro++;
printf("Suben: %d\n", dentro);
}
}
void bajan(void) {
for (int i = 0; i < M; i++) {
dentro = dentro - 2;
printf("Bajan: %d\n", dentro);
}
}
int main(void) {
suben(); /* no pasamos nada: usan la global */
bajan();
printf("Al final: %d viajeros\n", dentro);
return 0;
}
Salida:
Suben: 1 Suben: 2 Suben: 3 Suben: 4 Suben: 5 Bajan: 3 Bajan: 1 Al final: 1 viajeros
Funciona: 5 viajeros suben de uno en uno y luego bajan dos, dos veces (5 − 2 − 2 = 1). Pero mira las llamadas: suben() y bajan() no dicen nada de lo que tocan. En un programa grande, una global que cualquiera puede cambiar es una fuente de errores difíciles de seguir (y en el tema de hilos verás que es justo la variable compartida que provoca las condiciones de carrera). La versión con funciones limpias sería void suben(int *dentro, int n): la firma dice qué modifica.
Las constantes con #define (como N y M) no son variables globales: el preprocesador las sustituye por su valor antes de compilar. Usarlas para tamaños y límites es buena práctica.
Un programa completo con todo junto
Prototipos arriba, main en medio, definiciones al final, funciones que devuelven un valor, funciones void que modifican el vector y una que llama a otra pasándole las direcciones de dos casillas:
#include <stdio.h>
/* Prototipos */
int maximo(int v[], int n);
int minimo(int v[], int n);
double media(int v[], int n);
void intercambiar(int *a, int *b);
void ordenar(int v[], int n);
void mostrar(const char *titulo, int v[], int n);
int main(void) {
int viajeros[8] = {42, 7, 19, 3, 85, 26, 11, 54}; /* por parada, línea 201 */
int n = 8;
mostrar("Original: ", viajeros, n);
printf("Máximo: %d\n", maximo(viajeros, n));
printf("Mínimo: %d\n", minimo(viajeros, n));
printf("Media: %.2f\n", media(viajeros, n));
ordenar(viajeros, n); /* cambia el original */
mostrar("Ordenado: ", viajeros, n);
return 0;
}
int maximo(int v[], int n) {
int m = v[0];
for (int i = 1; i < n; i++)
if (v[i] > m) m = v[i];
return m;
}
int minimo(int v[], int n) {
int m = v[0];
for (int i = 1; i < n; i++)
if (v[i] < m) m = v[i];
return m;
}
double media(int v[], int n) {
int suma = 0;
for (int i = 0; i < n; i++)
suma += v[i];
return (double)suma / n;
}
void intercambiar(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
/* Burbuja: en cada pasada el mayor que queda sube al final */
void ordenar(int v[], int n) {
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (v[j] > v[j + 1])
intercambiar(&v[j], &v[j + 1]); /* & de cada casilla */
}
void mostrar(const char *titulo, int v[], int n) {
printf("%s", titulo);
for (int i = 0; i < n; i++)
printf("%d ", v[i]);
printf("\n");
}
Salida:
Original: 42 7 19 3 85 26 11 54 Máximo: 85 Mínimo: 3 Media: 30.88 Ordenado: 3 7 11 19 26 42 54 85
Comprobación: 42 + 7 + 19 + 3 + 85 + 26 + 11 + 54 = 247 viajeros, y 247 / 8 = 30,875, que %.2f redondea a 30,88. Lo más interesante es ordenar (el método de la burbuja): cuando dos casillas seguidas están desordenadas llama a intercambiar(&v[j], &v[j + 1]). Aunque v ya es un puntero, v[j] es un int, así que hace falta el & para pasar la dirección de esa casilla concreta.
Errores típicos con funciones
- Llamar a una función antes de declararla.
implicit declaration: error en gcc 14. Prototipo arriba. - Esperar que una función cambie una variable pasada por valor. Si tiene que modificarla, el parámetro es un puntero y la llamada lleva
&. - Olvidar el
&en la llamada a una función que recibe punteros. - Calcular el tamaño de un vector con
sizeofdentro de la función. Pasa siemprencomo parámetro. - Un camino sin
returnen una función que devuelve algo. - Usar el resultado de una función
void, o ignorar el de una que sí devuelve algo importante (como el 1/0 decobrar, o lo que devuelvescanf). void mainen lugar deint main(void)conreturn 0;.
Míralo paso a paso en Python Tutor
Python Tutor dibuja la pila de llamadas: cada función en su propio marco, con sus variables, y los punteros como flechas. Elige C y pega este programa, que hace una llamada por valor y otra por referencia:
#include <stdio.h>
void cobrar_copia(int saldo, int precio) {
saldo = saldo - precio;
}
int cobrar(int *saldo, int precio) {
if (*saldo < precio) {
return 0;
}
*saldo = *saldo - precio;
return 1;
}
int main(void) {
int saldo = 400;
cobrar_copia(saldo, 155);
int ok = cobrar(&saldo, 155);
printf("ok = %d, saldo = %d\n", ok, saldo);
return 0;
}
Qué observar paso a paso:
- Al entrar en
cobrar_copiaaparece un marco nuevo con susaldo(400) yprecio. Hay dos variablessaldoen pantalla, una en cada marco. - En
saldo = saldo - precio;cambia la del marco decobrar_copia(245). La demainsigue en 400. - Al salir, el marco desaparece entero y con él el 245.
- Al entrar en
cobrar, susaldoya no es un número: es una flecha que apunta alsaldodemain. - En
*saldo = *saldo - precio;cambia la caja demain(de 400 a 245), yokrecibe el 1 delreturn. Debe imprimirok = 1, saldo = 245.
Resumen
| Quiero… | Código | Ejemplo con la guagua |
|---|---|---|
| Una función que devuelve un valor | int precio_billete(int zonas) | 195 céntimos con 3 zonas |
| Una función que no devuelve nada | void separador(int largo) | dibuja la línea |
| Una función sin parámetros | int primera_salida(void) | 610 |
| Usarla antes de definirla | prototipo int precio_billete(int zonas); arriba | sin «implicit declaration» |
| Cambiar una variable de main | void cobrar(int *saldo, …) y cobrar(&saldo, …) | el saldo baja de verdad |
| Devolver dos resultados | uno por return y otro por puntero | cobrar devuelve 1/0 y cambia el saldo |
| Pasar un vector | int v[] (o int *v) y n aparte | total_subidas(subidas, 5) |
| Un vector que no se toca | const int v[] | solo lectura |
Tres ideas para quedarte:
- Una función de C recibe copias: si tiene que modificar algo de quien la llama, recibe su dirección.
- El compilador lee de arriba abajo: prototipos arriba, y
voidcuando no hay valor de retorno o no hay parámetros. - Un vector viaja como la dirección de su primera casilla: la función puede cambiarlo, pero no sabe su tamaño.
Ahora toca practicar con programas completos. En la práctica de funciones en C escribimos funciones de búsqueda que devuelven dos resultados, funciones que transforman vectores en su sitio y una calculadora de estadísticas de la línea 201 que lee los datos con scanf.
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




El hardware que sostiene al sistema operativo — teoría con la práctica de IC2 en la impresora, una interrupción que lo para todo y una caché que acierta (y el bucle infinito que no colgó mi portátil)