Punteros y cadenas en C: guía práctica de ejercicios

  1. Extremos de un arreglo mediante punteros

El siguiente programa solicita cinco enteros, los almacena en un arreglo y, mediante una función auxiliar, obtiene el valor menor y el mayor. La clave está en pasar dos punteros para que la función pueda modificar las variables del main.

#include <stdio.h>
#define TAM 5

void leer(int v[], int n);
void mostrar(const int v[], int n);
void extremos(const int v[], int n, int *minimo, int *maximo);

int main(void) {
    int datos[TAM];
    int menor, mayor;

    printf("Introduzca %d enteros:\n", TAM);
    leer(datos, TAM);

    printf("Valores ingresados: ");
    mostrar(datos, TAM);

    extremos(datos, TAM, &menor, &mayor);
    printf("Menor = %d, Mayor = %d\n", menor, mayor);

    return 0;
}

void leer(int v[], int n) {
    int i;
    for (i = 0; i < n; i++)
        scanf("%d", &v[i]);
}

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

void extremos(const int v[], int n, int *minimo, int *maximo) {
    int i;
    *minimo = *maximo = v[0];
    for (i = 1; i < n; i++) {
        if (v[i] < *minimo)
            *minimo = v[i];
        else if (v[i] > *maximo)
            *maximo = v[i];
    }
}

La función extremos recibe el arreglo y dos punteros. Al invocarla con &menor y &mayor, los punteros apuntan a las variables locales del main, permitiendo devolver ambos resultados sin usar return.

  1. Devolver una dirección desde una función

En este ejemplo se localiza el mayor valor y se retorna un puntero a dicho elemento. Es importante retornar la dirección de un dato que siga vivo tras finalizar la función, como un elemento del propio arreglo recibido.

#include <stdio.h>
#define TAM 5

void leer(int v[], int n);
void mostrar(const int v[], int n);
int *buscar_mayor(int v[], int n);

int main(void) {
    int datos[TAM];
    int *mayor;

    printf("Ingrese %d enteros:\n", TAM);
    leer(datos, TAM);

    printf("Lista: ");
    mostrar(datos, TAM);

    mayor = buscar_mayor(datos, TAM);
    printf("El mayor es: %d\n", *mayor);

    return 0;
}

void leer(int v[], int n) {
    int i;
    for (i = 0; i < n; i++)
        scanf("%d", &v[i]);
}

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

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

El valor de retorno es seguro porque v es un arreglo externo a la función, cuyo espacio persiste. Retornar la dirección de una variable local automática sería un error, ya que esa memoria se libera al salir de la función.

  1. Arreglos de caracteres versus punteros a constantes

Compararemos dos enfoques para manejar cadenas: un arreglo de caracteres con su propio espacio y un puntero que referencia una constante de cadena.

#include <stdio.h>
#include <string.h>
#define N 80

int main(void) {
    char a[N] = "Aprender C es divertido";
    char b[N] = "Aprender C es util";
    char aux[N];

    printf("sizeof(a) = %zu\n", sizeof(a));
    printf("strlen(a) = %zu\n", strlen(a));

    printf("Antes:\na: %s\nb: %s\n", a, b);

    strcpy(aux, a);
    strcpy(a, b);
    strcpy(b, aux);

    printf("Despues:\na: %s\nb: %s\n", a, b);

    return 0;
}

sizeof(a) devuelve el tamaño total del arreglo, incluyendo el terminador y el espacio no usado, mientras que strlen(a) cuenta solo los caracteres hasta el '\0'. No es posible asignar una cadena literal a un arreglo después de su definición con el operador =; debe usarse strcpy o inicializar en la declaración.

Con punteros el intercamboi se simplifica, pero la cadena apuntada no debe modificarse:

char *a = "Aprender C es divertido";
char *b = "Aprender C es util";
char *tmp;

tmp = a;
a = b;
b = tmp;

Aquí solo cambia la dirección almacenada en cada puntero; las cadenas constantes permanecen en el mismo lugar de memoria.

  1. Recorrido de arreglos bidimensionales con punteros

Se muestran tres formas de acceder a una matriz de dos filas y cuatro columnas: índices directos, puntero a entero y puntero a arreglo de cuatro enteros.

#include <stdio.h>

int main(void) {
    int matriz[2][4] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
    int i, j;
    int *p1;
    int (*p2)[4];

    printf("Acceso con indices:\n");
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 4; j++)
            printf("%d ", matriz[i][j]);
        printf("\n");
    }

    printf("\nAcceso con puntero a elemento:\n");
    for (p1 = &matriz[0][0], i = 0; p1 < &matriz[0][0] + 8; p1++, i++) {
        printf("%d ", *p1);
        if ((i + 1) % 4 == 0)
            printf("\n");
    }

    printf("\nAcceso con puntero a arreglo:\n");
    for (p2 = matriz; p2 < matriz + 2; p2++) {
        for (j = 0; j < 4; j++)
            printf("%d ", *(*p2 + j));
        printf("\n");
    }

    return 0;
}

La declaración int (*p2)[4] define un puntero a un arreglo de cuatro enteros. No debe confundirse con int *p2[4], que sería un arreglo de cuatro punteros a entero.

  1. Sustitución de caracteres en una cadena

La siguiente función recorre una cadena con un puntero y reemplaza cada aparición de un carácter por otro.

#include <stdio.h>
#define N 80

void reemplazar(char *s, char viejo, char nuevo);

int main(void) {
    char texto[N] = "Programar en C es apasionante.";

    printf("Original: %s\n", texto);
    reemplazar(texto, 'a', '*');
    printf("Modificado: %s\n", texto);

    return 0;
}

void reemplazar(char *s, char viejo, char nuevo) {
    while (*s) {
        if (*s == viejo)
            *s = nuevo;
        s++;
    }
}

La condición while (*s) es equivalente a while (*s != '\0'), porque el carácter nulo tiene valor numérico cero.

  1. Truncado de cadenas

Esta función detiene el recorrido al encontrar el carácter indicado y coloca un terminador nulo en esa posición, eliminando desde ese punto en adelante.

#include <stdio.h>
#define N 80

char *truncar(char *s, char c);

int main(void) {
    char s[N];
    char ch;

    printf("Introduzca una cadena: ");
    fgets(s, N, stdin);

    printf("Introduzca el caracter de corte: ");
    ch = getchar();

    truncar(s, ch);
    printf("Resultado: %s\n", s);

    return 0;
}

char *truncar(char *s, char c) {
    char *p = s;
    while (*p != '\0' && *p != c)
        p++;
    *p = '\0';
    return s;
}
  1. Validación de un identificador

El siguiente ejemplo verifica si una cadena cumple un formato específico: longitud fija, diecisiete dígitos y un último carácter que puede ser un dígito o una X.

#include <stdio.h>
#include <string.h>

int validar_id(const char *s);

int main(void) {
    const char *ids[] = {
        "31010120000721656X",
        "3301061996X0203301",
        "53010220051126571",
        "510104199211197977",
        "53010220051126133Y"
    };
    int i, n = sizeof(ids) / sizeof(ids[0]);

    for (i = 0; i < n; i++)
        printf("%s\t%s\n", ids[i], validar_id(ids[i]) ? "Valido" : "Invalido");

    return 0;
}

int validar_id(const char *s) {
    int i, n = strlen(s);
    if (n != 18)
        return 0;

    for (i = 0; i < 17; i++)
        if (s[i] < '0' || s[i] > '9')
            return 0;

    char ult = s[17];
    if (!((ult >= '0' && ult <= '9') || ult == 'X'))
        return 0;

    return 1;
}
  1. Codificación y decodificación tipo César

Se implementan dos funciones: codificar desplaza cada letra n posiciones hacia adelante, y decodificar realiza el desplazamiento inverso.

#include <stdio.h>
#define N 80

void codificar(char *s, int n);
void decodificar(char *s, int n);

int main(void) {
    char texto[N];
    int n;

    printf("Texto a codificar: ");
    fgets(texto, N, stdin);

    printf("Valor de desplazamiento: ");
    scanf("%d", &n);

    codificar(texto, n);
    printf("Codificado: %s\n", texto);

    decodificar(texto, n);
    printf("Decodificado: %s\n", texto);

    return 0;
}

void codificar(char *s, int n) {
    while (*s) {
        if (*s >= 'a' && *s <= 'z')
            *s = (*s - 'a' + n) % 26 + 'a';
        else if (*s >= 'A' && *s <= 'Z')
            *s = (*s - 'A' + n) % 26 + 'A';
        s++;
    }
}

void decodificar(char *s, int n) {
    while (*s) {
        if (*s >= 'a' && *s <= 'z')
            *s = (*s - 'a' - n + 26) % 26 + 'a';
        else if (*s >= 'A' && *s <= 'Z')
            *s = (*s - 'A' - n + 26) % 26 + 'A';
        s++;
    }
}
  1. Ordenamiento de argumentos de línea de comandos

El programa recibe parámetros por consola, ordena sus direcciones mediante el algoritmo de burbuja y muestra el resultado.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    int i, j;
    char *tmp;

    for (i = 1; i < argc - 1; i++) {
        for (j = i + 1; j < argc; j++) {
            if (strcmp(argv[i], argv[j]) > 0) {
                tmp = argv[i];
                argv[i] = argv[j];
                argv[j] = tmp;
            }
        }
    }

    for (i = 1; i < argc; i++)
        printf("Hola, %s\n", argv[i]);

    return 0;
}

Etiquetas: C Punteros strings-en-C arreglos-multidimensionales argc-argv

Publicado el 6-30 21:00