Funciones en C — práctica con 3 programas de la guagua: búsquedas con dos resultados, vectores que se transforman y las estadísticas de la línea (con scanf)

Hay un momento en IC2 en que el profesor escribe en la pizarra dos funciones casi idénticas y pregunta cuál modifica el vector original. Una recibe int v[] y la otra int *v. Casi toda la clase señala la segunda, convencida de que el asterisco marca la diferencia. Las dos lo modifican: un vector en C siempre viaja como una dirección, lo declares como lo declares. Esta práctica va de convertir esa idea, y la de pasar variables por referencia, en herramientas para resolver problemas de verdad.

Es la parte práctica de la teoría de funciones en C. Son tres programas completos de nivel medio, todos con la línea 201 de la guagua y con el estilo de los laboratorios de IC2 (prototipos arriba, main en medio y definiciones al final): funciones de búsqueda sobre los retrasos de los viajes, funciones que transforman vectores en su sitio y una calculadora de estadísticas que lee los viajeros de cada parada con scanf. Los datos tienen lo que tienen los de verdad: repetidos, negativos y huecos. Todas las salidas son de una ejecución real con gcc 14.

Antes de empezar

Conviene tener claro:

  • Cómo se escriben un prototipo, una definición y una llamada, y qué significa void.
  • La diferencia entre pasar x (una copia) y pasar &x (la dirección) a un parámetro int *.
  • Que un vector llega a la función como la dirección de su primera casilla, y que su tamaño hay que pasarlo aparte.
  • Qué es return y que solo puede devolver un valor.

Si alguno te suena dudoso, repasa la teoría (y la de punteros en C) antes de seguir.

Compila cada programa con gcc -Wall -std=c11 programa.c -o programa. Con -Wall gcc te avisa de casi todos los errores de este tema: llamadas sin prototipo, caminos sin return y sizeof sobre un parámetro vector. Puedes probarlos también en el entorno de C de la web; el tercero lee del teclado, así que escribe los datos en la pestaña Entrada.

Programa 1: funciones de búsqueda en vectores

Tenemos los retrasos, en minutos, de los 10 viajes de la línea 201 de un día. Queremos varias consultas: en qué viaje hubo un retraso concreto, cuál fue el peor y el mejor (y en qué viaje), cuántas veces se repite un retraso y si los datos están ordenados. Cada consulta será una función.

La idea

Una búsqueda tiene que devolver dos cosas: si ha encontrado el dato y dónde. Hay dos patrones, y en IC2 salen los dos:

  • Patrón 1: devolver la posición con return y usar -1 como señal de «no está», porque nunca es una posición válida.
  • Patrón 2: devolver 1 o 0 con return y dejar la posición en una variable de main a través de un puntero (int *pos).

El máximo y el mínimo usan el segundo patrón al revés: el valor sale por return y la posición, por el puntero.

Antes de programar, escribe la firma de cada función y señala qué parámetros son de entrada (el vector, n, el valor buscado) y cuáles de salida (int *pos). Las de salida llevarán & en la llamada.

El código

#include <stdio.h>

/* Prototipos: arriba, para que main pueda usarlas */
int buscar(int v[], int n, int buscado);
int buscar_pos(int v[], int n, int buscado, int *pos);
int maximo_con_pos(int v[], int n, int *pos);
int minimo_con_pos(int v[], int n, int *pos);
int contar(int v[], int n, int valor);
int esta_ordenado(int v[], int n);
void mostrar(int v[], int n);

int main(void) {
    int retrasos[10] = {42, 7, 19, 7, 85, 26, 7, 54, 13, 7};   /* minutos, 10 viajes */
    int ordenados[5] = {1, 3, 5, 7, 9};
    int n = 10, pos, r;

    printf("Retrasos: ");
    mostrar(retrasos, n);

    r = buscar(retrasos, n, 85);                  /* patrón 1: return con -1 */
    if (r != -1) printf("85 min: viaje %d\n", r);
    r = buscar(retrasos, n, 99);
    if (r == -1) printf("99 min: ningún viaje\n");

    if (buscar_pos(retrasos, n, 26, &pos))        /* patrón 2: 1/0 y posición por puntero */
        printf("26 min: viaje %d\n", pos);

    int max = maximo_con_pos(retrasos, n, &pos);
    printf("\nPeor retraso:   %d min (viaje %d)\n", max, pos);
    int min = minimo_con_pos(retrasos, n, &pos);
    printf("Mejor retraso:  %d min (viaje %d)\n", min, pos);
    printf("Viajes con 7 min: %d\n", contar(retrasos, n, 7));

    printf("\n¿Retrasos ordenados? %s\n", esta_ordenado(retrasos, n) ? "Sí" : "No");
    printf("¿{1,3,5,7,9} ordenado? %s\n", esta_ordenado(ordenados, 5) ? "Sí" : "No");
    return 0;
}

/* Posición de la primera aparición, o -1 (un índice imposible) */
int buscar(int v[], int n, int buscado) {
    for (int i = 0; i < n; i++)
        if (v[i] == buscado)
            return i;                     /* la primera y me voy */
    return -1;
}

/* Devuelve 1/0 y deja la posición en *pos: dos resultados */
int buscar_pos(int v[], int n, int buscado, int *pos) {
    for (int i = 0; i < n; i++)
        if (v[i] == buscado) {
            *pos = i;                     /* escribo en la variable de main */
            return 1;
        }
    return 0;                             /* *pos no se toca */
}

/* El valor por return; la posición, por puntero */
int maximo_con_pos(int v[], int n, int *pos) {
    int p = 0;
    for (int i = 1; i < n; i++)
        if (v[i] > v[p]) p = i;
    *pos = p;
    return v[p];
}

int minimo_con_pos(int v[], int n, int *pos) {
    int p = 0;
    for (int i = 1; i < n; i++)
        if (v[i] < v[p]) p = i;           /* < y no <=: me quedo con el primero */
    *pos = p;
    return v[p];
}

int contar(int v[], int n, int valor) {
    int c = 0;
    for (int i = 0; i < n; i++)
        if (v[i] == valor) c++;
    return c;
}

/* 1 si cada elemento es <= que el siguiente */
int esta_ordenado(int v[], int n) {
    for (int i = 0; i < n - 1; i++)       /* n - 1: comparo con v[i + 1] */
        if (v[i] > v[i + 1])
            return 0;
    return 1;
}

void mostrar(int v[], int n) {
    for (int i = 0; i < n; i++)
        printf("%d ", v[i]);
    printf("\n");
}

Salida

Retrasos: 42 7 19 7 85 26 7 54 13 7 
85 min: viaje 4
99 min: ningún viaje
26 min: viaje 5

Peor retraso:   85 min (viaje 4)
Mejor retraso:  7 min (viaje 1)
Viajes con 7 min: 4

¿Retrasos ordenados? No
¿{1,3,5,7,9} ordenado? Sí

Compruébalo con los datos {42, 7, 19, 7, 85, 26, 7, 54, 13, 7}: el 85 está en la casilla 4 y el 26 en la 5; el 99 no está y buscar devuelve -1. El peor retraso es 85 (viaje 4); el mejor, 7, aparece en los viajes 1, 3, 6 y 9, y la función se queda con el primero porque compara con < y no con <=. El 7 sale 4 veces. Los retrasos no están ordenados (42 > 7 en la primera comparación) y {1, 3, 5, 7, 9} sí.

Una función, dos resultadosVector de diez retrasos con el 85 en la casilla 4. La función devuelve 85 por return y escribe 4 en la variable pos de main a través del puntero.retrasos[10]4271978526754137[0][1][2][3][4][5][6][7][8][9]maximo_con_pos(retrasos, 10, &pos)return → 85el valor*pos = 4la posición, en main
return saca un valor; cada puntero que recibe la función es una salida más.

Traza de maximo_con_pos, que guarda la posición del mejor visto hasta ahora (p), no su valor:

iv[i]v[p] antes¿v[i] > v[p]?p después
1742No0
21942No0
3742No0
48542Sí4
5…926, 7, 54, 13, 785No4

Al final, *pos = 4 escribe en la pos de main y return v[4] devuelve 85.

Preguntas que te puedes hacer

¿Por qué -1 y no 0 para «no encontrado»? Porque 0 es una posición válida: el primer viaje. Si buscar devolviera 0 cuando no encuentra nada, no sabrías si el dato está en la casilla 0 o no está.

¿Por qué en buscar_pos no se toca *pos si no lo encuentra? Porque la función promete solo una cosa cuando devuelve 0: que no está. main no debe leer pos en ese caso, y por eso lo usa dentro del if. Tocar *pos con un valor inventado solo serviría para disimular errores.

¿Por qué esta_ordenado recorre hasta n - 1? Porque compara cada casilla con la siguiente, v[i + 1]. Si i llegara a n - 1, v[i + 1] sería la casilla n, que no existe.

¿Puede una función llamar a mostrar si está definida después? Sí, porque su prototipo está arriba. El orden de las definiciones da igual mientras todos los prototipos vayan antes de la primera llamada.

Llamar a buscar_pos(retrasos, n, 26, pos) sin &. pos es un int sin valor y la función lo usaría como dirección. gcc 14 lo convierte en un error de compilación (int donde se espera int *).

Pruébalo tú

  • Escribe int buscar_ultimo(int v[], int n, int x), que devuelva la última aparición recorriendo el vector de atrás hacia delante.
  • Haz que minimo_con_pos se quede con la última aparición del mínimo. ¿Qué único carácter tienes que cambiar?

Programa 2: funciones que transforman vectores

Ahora las funciones no consultan: cambian el vector. Damos la vuelta a las salidas del día, las rotamos (el primer turno pasa al final, o el último al principio), filtramos los cambios positivos de viajeros en un vector nuevo y quitamos los números de línea repetidos de una lista.

La idea

Como el vector llega como dirección, cualquier v[i] = … dentro de la función cambia el de main: no hace falta return. Lo delicado son dos funciones que además cambian un tamaño:

  • filtrar_positivos crea un vector nuevo y tiene que decirle a main cuántos elementos ha metido: recibe int *m.
  • quitar_repetidos hace encoger el propio vector: recibe int *n y lo decrementa cada vez que tapa un hueco.

Y rotar_k reutiliza rotar_derecha: una función llamando a otra en lugar de repetir código.

Antes de programar, dibuja las casillas y decide la dirección de cada recorrido. Si vas a correr los elementos a la derecha, recorre de atrás hacia delante; si los corres a la izquierda, de delante hacia atrás. En la dirección equivocada, el primer valor se copia en todas las casillas.

El código

#include <stdio.h>

void intercambiar(int *a, int *b);
void invertir(int v[], int n);
void rotar_derecha(int v[], int n);
void rotar_izquierda(int v[], int n);
void rotar_k(int v[], int n, int k);
void filtrar_positivos(int v[], int n, int res[], int *m);
void quitar_repetidos(int v[], int *n);
void mostrar(const char *titulo, int v[], int n);

int main(void) {
    int salidas[8] = {10, 20, 30, 40, 50, 60, 70, 80};    /* minutos tras las 9:00 */
    mostrar("Salidas:         ", salidas, 8);
    invertir(salidas, 8);
    mostrar("Invertidas:      ", salidas, 8);
    invertir(salidas, 8);                                 /* otra vez: como estaban */
    rotar_derecha(salidas, 8);
    mostrar("Rotar derecha:   ", salidas, 8);
    rotar_izquierda(salidas, 8);
    rotar_izquierda(salidas, 8);
    mostrar("Rotar izq. x2:   ", salidas, 8);

    int zonas[5] = {1, 2, 3, 4, 5};
    rotar_k(zonas, 5, 2);
    mostrar("Zonas, rotar 2:  ", zonas, 5);

    int cambio[8] = {-3, 7, -1, 15, -8, 4, 0, 12};        /* suben menos bajan */
    int positivos[8], m;
    filtrar_positivos(cambio, 8, positivos, &m);          /* &m: la función lo rellena */
    printf("Positivos (%d):   ", m);
    mostrar("", positivos, m);

    int lineas[9] = {201, 202, 201, 301, 202, 201, 303, 301, 210};
    int n = 9;
    quitar_repetidos(lineas, &n);                         /* &n: el tamaño cambia */
    printf("Líneas (%d):      ", n);
    mostrar("", lineas, n);
    return 0;
}

void intercambiar(int *a, int *b) {
    int t = *a; *a = *b; *b = t;
}

/* Solo hasta la mitad: n / 2 intercambios */
void invertir(int v[], int n) {
    for (int i = 0; i < n / 2; i++)
        intercambiar(&v[i], &v[n - 1 - i]);
}

/* [1,2,3,4] -> [4,1,2,3] */
void rotar_derecha(int v[], int n) {
    int ultimo = v[n - 1];
    for (int i = n - 1; i > 0; i--)       /* de atrás hacia delante */
        v[i] = v[i - 1];
    v[0] = ultimo;
}

/* [1,2,3,4] -> [2,3,4,1] */
void rotar_izquierda(int v[], int n) {
    int primero = v[0];
    for (int i = 0; i < n - 1; i++)       /* de delante hacia atrás */
        v[i] = v[i + 1];
    v[n - 1] = primero;
}

/* Reutiliza otra función: k rotaciones de una casilla */
void rotar_k(int v[], int n, int k) {
    for (int i = 0; i < k % n; i++)
        rotar_derecha(v, n);
}

/* Copia los positivos a res y deja en *m cuántos hay */
void filtrar_positivos(int v[], int n, int res[], int *m) {
    *m = 0;
    for (int i = 0; i < n; i++)
        if (v[i] > 0) {
            res[*m] = v[i];
            (*m)++;                       /* paréntesis: el valor, no el puntero */
        }
}

/* Deja cada número una vez, en el orden en que aparece */
void quitar_repetidos(int v[], int *n) {
    int i = 0;
    while (i < *n) {
        int j = i + 1;
        while (j < *n) {
            if (v[j] == v[i]) {
                for (int k = j; k < *n - 1; k++)   /* tapo el hueco */
                    v[k] = v[k + 1];
                (*n)--;                            /* el vector encoge */
            } else {
                j++;
            }
        }
        i++;
    }
}

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

Salidas:         10 20 30 40 50 60 70 80 
Invertidas:      80 70 60 50 40 30 20 10 
Rotar derecha:   80 10 20 30 40 50 60 70 
Rotar izq. x2:   20 30 40 50 60 70 80 10 
Zonas, rotar 2:  4 5 1 2 3 
Positivos (4):   7 15 4 12 
Líneas (5):      201 202 301 303 210 
  • Invertir hace 8 / 2 = 4 intercambios (10↔80, 20↔70, 30↔60, 40↔50). Invertir dos veces deja el vector como estaba.
  • Rotar a la derecha una vez: el 80 pasa delante. Rotar a la izquierda dos veces sobre eso: el 80 vuelve al final y el 10 se va detrás de él. Resultado: 20 … 80 10.
  • Rotar 2 las zonas {1, 2, 3, 4, 5}: dos rotaciones a la derecha, 4 5 1 2 3. El k % n hace que rotar 7 sea lo mismo que rotar 2.
  • Filtrar: de {-3, 7, -1, 15, -8, 4, 0, 12} pasan 7, 15, 4 y 12 (el 0 no es positivo) y m vale 4.
  • Quitar repetidos: de nueve números de línea quedan cinco, en el orden en que aparecieron.
Filtrar los positivosLas cuatro casillas positivas del vector de cambios se copian, en orden, al vector de resultado, y el contador m de main sube de 0 a 4.cambio[8]-37-115-84012positivos[ ]715412*m tras cada copia:0 → 1 → 2 → 3 → 4(*m)++ suma al contador de main; *m++ movería el puntero
La función deja el resultado en un vector de main y su tamaño en *m.

Traza de quitar_repetidos sobre {201, 202, 201, 301, 202, 201, 303, 301, 210}:

iv[i]Repetidos que tapaVector después*n
0201casillas 2 y 5201 202 301 202 303 301 2107
1202casilla 3201 202 301 303 301 2106
2301casilla 4201 202 301 303 2105
3303ningunoigual5
4210ningunoigual5

Fíjate en que, cuando tapa un hueco, j no avanza: en la casilla j acaba de entrar el siguiente número y hay que comprobarlo también.

Preguntas que te puedes hacer

¿Por qué (*m)++ y no *m++? Porque ++ va antes que *: *m++ avanzaría el puntero m (la copia local de la dirección) y el contador de main se quedaría en 0. Con los paréntesis se incrementa el valor apuntado.

¿Por qué res[*m] = v[i]? Porque *m es a la vez el contador y la primera casilla libre del vector de resultado: con 0 positivos se escribe en la casilla 0, con 1 en la 1…

¿Por qué filtrar_positivos necesita un vector de 8 si solo salen 4? Porque la función no sabe de antemano cuántos va a encontrar: en el peor caso todos son positivos. El vector de destino tiene que tener sitio para todos.

¿Por qué rotar_k hace k % n rotaciones? Rotar n veces un vector de n casillas lo deja igual. Con el resto, rotar 12 veces un vector de 5 es rotarlo 2, y te ahorras 10 vueltas inútiles.

Pasar n por valor a quitar_repetidos. La función encogería su copia, main seguiría creyendo que hay 9 números y mostraría los restos que quedan al final del vector.

Pruébalo tú

  • Escribe void escalar(int v[], int n, int factor), que multiplique cada casilla, y úsala para pasar los minutos a segundos.
  • Cambia quitar_repetidos para que devuelva con return cuántos números ha quitado, además de actualizar *n.

Programa 3: calculadora de estadísticas de la línea

El último programa junta todo: lee del teclado cuántas paradas hay y cuántos viajeros subieron en cada una, y calcula la media, la mediana, la moda (con cuántas veces se repite) y la varianza. Cada estadística es una función, y una de ellas llama a otra.

La idea

leer_datos rellena el vector de main con scanf y devuelve cuántos datos ha leído (0 si algo falla, para que main pare). Las estadísticas son funciones que devuelven un double. Dos decisiones importantes:

  • La mediana necesita ordenar, pero si ordenara el vector de main lo dejaría cambiado para las demás funciones. Trabaja sobre una copia local.
  • La moda devuelve dos cosas: el valor (por return) y cuántas veces aparece (por el puntero int *frecuencia).

Antes de programar, decide qué funciones pueden cambiar el vector y cuáles no. Solo leer_datos debería escribir en él; para las demás, puedes declarar el parámetro const int v[] y gcc te protegerá.

El código

#include <stdio.h>
#define MAX 100

int leer_datos(int v[], int max);
double media(int v[], int n);
double mediana(int v[], int n);
int moda(int v[], int n, int *frecuencia);
double varianza(int v[], int n);
void ordenar(int v[], int n);

int main(void) {
    int viajeros[MAX];
    int n = leer_datos(viajeros, MAX);
    if (n == 0) return 1;                  /* sin datos no hay estadística */

    int veces;
    int m = moda(viajeros, n, &veces);     /* valor por return, veces por puntero */

    printf("\n=== ESTADÍSTICAS DE LA LÍNEA 201 ===\n");
    printf("Paradas:   %d\n", n);
    printf("Media:     %.2f\n", media(viajeros, n));
    printf("Mediana:   %.1f\n", mediana(viajeros, n));
    printf("Moda:      %d (%d veces)\n", m, veces);
    printf("Varianza:  %.2f\n", varianza(viajeros, n));
    printf("Datos sin tocar: ");
    for (int i = 0; i < n; i++) printf("%d ", viajeros[i]);
    printf("\n");
    return 0;
}

/* Lee cuántos datos hay y los datos; devuelve n (0 si algo falla) */
int leer_datos(int v[], int max) {
    int n;
    printf("¿Cuántas paradas? (1-%d): ", max);
    if (scanf("%d", &n) != 1 || n < 1 || n > max) {
        printf("Número de paradas no válido\n");
        return 0;
    }
    for (int i = 0; i < n; i++) {
        printf("  Viajeros en la parada %d: ", i + 1);
        if (scanf("%d", &v[i]) != 1) return 0;   /* &v[i]: dirección de esa casilla */
    }
    return n;
}

double media(int v[], int n) {
    int suma = 0;
    for (int i = 0; i < n; i++) suma += v[i];
    return (double)suma / n;              /* el cast, ANTES de dividir */
}

/* Ordena una COPIA: el vector de main no se toca */
double mediana(int v[], int n) {
    int copia[MAX];
    for (int i = 0; i < n; i++) copia[i] = v[i];
    ordenar(copia, n);
    if (n % 2 == 1) return copia[n / 2];
    return (copia[n / 2 - 1] + copia[n / 2]) / 2.0;   /* par: media de los dos centrales */
}

int moda(int v[], int n, int *frecuencia) {
    int mejor = v[0];
    *frecuencia = 0;
    for (int i = 0; i < n; i++) {
        int c = 0;
        for (int j = 0; j < n; j++)
            if (v[j] == v[i]) c++;
        if (c > *frecuencia) {            /* > y no >=: gana el primero que aparece */
            *frecuencia = c;
            mejor = v[i];
        }
    }
    return mejor;
}

/* Llama a otra función: media */
double varianza(int v[], int n) {
    double m = media(v, n), s = 0;
    for (int i = 0; i < n; i++)
        s += (v[i] - m) * (v[i] - m);
    return s / n;
}

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]) {
                int t = v[j]; v[j] = v[j + 1]; v[j + 1] = t;
            }
}

Salida

Con los viajeros de 8 paradas (15, 8, 42, 8, 27, 19, 8 y 35):

¿Cuántas paradas? (1-100): 8
  Viajeros en la parada 1: 15
  Viajeros en la parada 2: 8
  Viajeros en la parada 3: 42
  Viajeros en la parada 4: 8
  Viajeros en la parada 5: 27
  Viajeros en la parada 6: 19
  Viajeros en la parada 7: 8
  Viajeros en la parada 8: 35

=== ESTADÍSTICAS DE LA LÍNEA 201 ===
Paradas:   8
Media:     20.25
Mediana:   17.0
Moda:      8 (3 veces)
Varianza:  151.94
Datos sin tocar: 15 8 42 8 27 19 8 35 

Comprobación a mano:

  • Media: 15 + 8 + 42 + 8 + 27 + 19 + 8 + 35 = 162, y 162 / 8 = 20,25.
  • Mediana: ordenados quedan 8 8 8 15 19 27 35 42. Con 8 datos (par) es la media de los dos centrales, (15 + 19) / 2 = 17,0.
  • Moda: el 8 aparece 3 veces; ningún otro se repite.
  • Varianza: la media de los cuadrados de las distancias a 20,25. Por ejemplo, el 42 aporta (42 − 20,25)² = 473,06; sumando las ocho y dividiendo entre 8 sale 151,94.
  • Datos sin tocar: el vector de main sigue en el orden en que se tecleó, porque la mediana ordenó su copia.
La mediana sobre una copiaArriba, los viajeros de main en su orden original. Abajo, la copia local ordenada, con las casillas 3 y 4 (15 y 19) resaltadas: su media es 17,0.viajeros (main, sin tocar)1584282719835copia + ordenarcopia ordenada (local de mediana)8881519273542(15 + 19) / 2.0 = 17.08 datos: la mediana es la media de los dos centrales
Ordenar la copia deja intacto el vector de main.

Traza de moda (solo las vueltas en las que cambia algo):

iv[i]Veces que aparece (c)¿c > *frecuencia?mejor / *frecuencia
0151Sí (1 > 0)15 / 1
183Sí (3 > 1)8 / 3
2…742, 8, 27, 19, 8, 351 o 3No8 / 3

Preguntas que te puedes hacer

¿Por qué &v[i] en el scanf? scanf necesita la dirección donde dejar el número. v[i] es el entero de la casilla i; &v[i] es su dirección. También valdría v + i.

¿Por qué leer_datos devuelve n en lugar de recibir int *n? Las dos formas funcionan. Devolverlo deja una llamada más limpia (int n = leer_datos(...)) y usa el 0 como señal de error. Lo importante es que el tamaño llegue a main por algún camino.

¿Por qué / 2.0 en la mediana? Con dos int, (15 + 18) / 2 daría 16 en lugar de 16,5. El 2.0 fuerza la división decimal.

¿Por qué varianza llama a media en lugar de calcularla otra vez? Para no repetir código: si un día cambias cómo se calcula la media, solo lo cambias en un sitio. Una función puede llamar a otra siempre que su prototipo esté arriba.

¿Qué pasa si escribo letras en vez de un número? scanf devuelve 0, leer_datos devuelve 0 y main termina con return 1 sin calcular nada. Mejor que dividir entre un n que no tiene valor.

Ordenar el vector original dentro de mediana. Las estadísticas saldrían bien, pero «Datos sin tocar» saldría ordenado y cualquier función que se llamara después vería los datos en otro orden. Si una función solo tiene que consultar, no debe dejar huella.

Pruébalo tú

  • Añade int paradas_sobre_media(const int v[], int n) y muestra cuántas paradas superan la media.
  • Haz que leer_datos vuelva a preguntar si el número de paradas no es válido, en lugar de terminar.

Errores típicos en esta práctica

  • Olvidar el & al llamar a una función con parámetros de salida: buscar_pos(v, n, x, pos).
  • *m++ en lugar de (*m)++ para incrementar un contador recibido por puntero.
  • Pasar el tamaño por valor a una función que lo cambia (quitar_repetidos).
  • Usar 0 como «no encontrado» cuando 0 es una posición válida.
  • Ordenar el vector original en una función que solo debería consultar.
  • Recorrer en la dirección equivocada al desplazar elementos, copiando el mismo valor en todas las casillas.

Míralo paso a paso en Python Tutor

En Python Tutor, eligiendo C, se ve muy bien una función que devuelve un valor por return y otro por puntero. Pega este programa:

#include <stdio.h>

int maximo_con_pos(int v[], int n, int *pos) {
    int p = 0;
    for (int i = 1; i < n; i++) {
        if (v[i] > v[p]) {
            p = i;
        }
    }
    *pos = p;
    return v[p];
}

int main(void) {
    int retrasos[5] = {42, 7, 85, 26, 13};
    int pos;
    int max = maximo_con_pos(retrasos, 5, &pos);
    printf("%d en el viaje %d\n", max, pos);
    return 0;
}

Qué observar paso a paso:

  1. Antes de la llamada, pos existe en el marco de main pero sin valor.
  2. Al entrar en maximo_con_pos, v y pos son dos flechas: una al vector de main y otra a su variable pos.
  3. Durante el for, solo cambia la variable local p: 0, y luego 2 cuando aparece el 85.
  4. En *pos = p; se rellena la caja pos de main, al otro lado de la flecha.
  5. Al volver, el return v[p] llega a max y el marco de la función desaparece. Debe imprimir 85 en el viaje 2.

Resumen

  • Un vector llega a la función como dirección: int v[] e int *v son lo mismo y las dos permiten modificar el original.
  • Para «devolver» varias cosas, una sale por return y las demás por punteros; en la llamada, las de salida llevan &.
  • Para «no encontrado», un valor que nunca sea válido (-1 para una posición) o un return de 1/0 con la posición por puntero.
  • Si una función cambia el tamaño de un vector, recibe int *n y usa (*n)-- o (*n)++.
  • Una función que solo consulta trabaja sobre una copia (o recibe const int v[]) para no dejar huella.
  • Una función puede reutilizar otra (rotar_k, varianza) si su prototipo está arriba.

Checklist: ¿lo has practicado?

  • ¿Has ejecutado los tres programas y comprobado las salidas?
  • ¿Sabes cuándo usar el patrón -1 y cuándo el de 1/0 con la posición por puntero?
  • ¿Sabes por qué (*m)++ necesita los paréntesis?
  • ¿Puedes explicar por qué la mediana trabaja sobre una copia?
  • ¿Has comprobado a mano la media y la mediana?
  • ¿Has intentado los «Pruébalo tú» sin mirar la teoría?

Si has respondido que sí a casi todo, estás listo para los ejercicios de funciones en C, donde ya no te doy el código hecho. ¡Nos vemos allí!

Siguiente paso: ejerciciosFunciones en C — ejercicios resueltos con la guagua: segundo peor retraso, paradas que se cierran y abren y horarios que se fusionan (más el chuletario para el examen)

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *