- 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.
- 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.
- 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.
- 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.
- 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.
- 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;
}
- 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;
}
- 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++;
}
}
- 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;
}