Universidad Tecnológica Nacional
Facultad Regional Mar del Plata
Conceptos claros, ejemplos visuales y buenas prácticas.
En C no existe un tipo de dato nativo llamado string. Para el hardware y el sistema operativo, las palabras son simplemente una secuencia de caracteres individuales uno al lado del otro. Una cadena de caracteres es un arreglo de tipo char con una particularidad: debe terminar con un carácter especial llamado terminador nulo.
'\0')El terminador es un carácter especial que simboliza el fin de una cadena. Es un byte con todos sus bits en cero (valor decimal 0). Cuando el sistema operativo o las funciones de la biblioteca estándar leen una cadena, se detienen al encontrar '\0'.
char texto[6] = "Hola";Diferencia clave: '\0' (cero numérico) no es lo mismo que '0' (el carácter dígito cero, cuyo valor ASCII es 48).
char nombre[20]; // arreglo de 20 char, capacidad para 19 caracteres + '\0'
char saludo[] = "hola mundo"; // tamaño automático: 11 (10 letras + '\0')
char patente[12] = "AB1234CD"; // las posiciones [8] a [11] se llenan con '\0'
Problema con scanf("%s", ...): se detiene en el primer espacio en blanco. Para leer líneas completas, lo más seguro y recomendado es usar fgets().
char nombre[20];
printf("Ingrese su nombre: ");
fgets(nombre, sizeof(nombre), stdin);
// Eliminar el salto de línea final ('\n') que fgets incluye
nombre[strcspn(nombre, "\n")] = '\0';
strcspn(cadena, "\n") devuelve la posición del primer '\n'. Reemplazarlo por '\0' elimina el salto de línea de forma simple.
Si el usuario ingresa más caracteres de los que permite el arreglo, fgets trunca la cadena y deja el resto en el buffer de entrada. Para mantener el buffer limpio, podemos usar esta función estándar:
void limpiarBuffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
Ejemplo de lectura robusta que maneja el desborde:
int scanString(char* destino, int maxLen) {
fgets(destino, maxLen, stdin);
int len = strlen(destino);
if (len > 0 && destino[len-1] == '\n')
destino[len-1] = '\0';
else
limpiarBuffer();
return strlen(destino);
}
Al ser un arreglo, podemos acceder a cada carácter mediante su índice. Esto refuerza la idea de que un string es un arreglo.
char saludo[] = "Ciencia";
for (int i = 0; i < strlen(saludo); i++) {
printf("saludo[%d] = %c\n", i, saludo[i]);
}
Un string individual es un arreglo unidimensional. Cuando necesitamos guardar varios strings (por ejemplo, una lista de nombres), estamos creando una matriz (arreglo bidimensional) de caracteres.
char nombres[3][20] = {"Ana", "Luis", "Carla"};
nombres[fila][columna]
printf("%c", nombres[0][0]); // Imprime 'A'
printf("%c", nombres[2][3]); // Imprime 'l' (de Carla)
Recorrido de una matriz de strings:
for (int i = 0; i < 3; i++) {
printf("Nombre %d: %s\n", i, nombres[i]);
for (int j = 0; nombres[i][j] != '\0'; j++) {
printf(" letra %d: %c\n", j, nombres[i][j]);
}
}
argv (argumentos de línea de comandos), que es una matriz de strings.
<string.h>Proporciona funciones esenciales para manipular cadenas.
strlen() - longitud de la cadenachar texto[] = "Hola";
int len = strlen(texto); // len = 4 (no cuenta el '\0')
strcpy() - copiar cadenaschar origen[] = "Mundo";
char destino[20];
strcpy(destino, origen); // destino = "Mundo"
strcpy no verifica que el destino tenga suficiente espacio. Si origen es más largo, se producirá un desbordamiento. Usa strncpy para mayor seguridad.
strcat() - concatenar cadenaschar mensaje[50] = "Hola ";
char mundo[] = "mundo";
strcat(mensaje, mundo); // mensaje = "Hola mundo"
strcmp() y strcmpi() - comparar cadenasstrcmp compara sensible a mayúsculas/minúsculas. strcmpi (no estándar, en algunos compiladores _stricmp) ignora diferencias de caso.
if (strcmp(palabra, "salir") == 0) {
printf("Son iguales");
}
| Comparación | Resultado |
|---|---|
strcmp("abc","def") | < 0 (negativo) |
strcmp("def","abc") | > 0 (positivo) |
strcmp("abc","abc") | 0 (iguales) |
strcmpi("HOLA","hola") | 0 (iguales, ignorando mayúsculas) |
char* y char[] (punto clave)Es común ver dos formas de declarar strings. Aunque parecen similares, tienen diferencias fundamentales en cuanto a mutabilidad y dónde se almacena la cadena.
char arr[])char arr[] = "texto";
arr[0] = 'T'; // válido, ahora "Texto"
char* ptr)char* ptr = "texto";
"texto" se almacena en una sección de memoria de solo lectura (normalmente el segmento de datos o código).ptr apunta a esa zona de solo lectura.ptr[0] = 'T'; // ERROR: intenta escribir en memoria de solo lectura
ptr = "otro"; // válido: ahora apunta a otro literal
char arr[] = "hola";char* ptr = "hola";char[]) cuando necesites modificar la cadena o cuando sea una variable local que cambiará su contenido.char*) para apuntar a cadenas fijas que no cambiarán, o cuando necesites reasignar el puntero a diferentes cadenas.En los parámetros de una función, char arr[] y char* arr son equivalentes (ambos reciben un puntero). Sin embargo, la distinción anterior aplica al contenido apuntado.
void funcion(char* param) { // también podría ser char param[]
param[0] = 'X'; // válido solo si param apunta a memoria modificable
}
Si llamas a funcion("texto"), el literal no podrá modificarse. Si llamas con un arreglo modificable, sí podrás.
#include <stdio.h>
#include <string.h>
#define CANT 3
#define LONG 50
void limpiarBuffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
int main() {
char personas[CANT][LONG];
int i;
for (i = 0; i < CANT; i++) {
printf("Nombre %d: ", i+1);
fgets(personas[i], LONG, stdin);
personas[i][strcspn(personas[i], "\n")] = '\0';
}
printf("\nLista de nombres:\n");
for (i = 0; i < CANT; i++) {
printf("%d. %s (longitud: %zu)\n", i+1, personas[i], strlen(personas[i]));
}
return 0;
}
'\0'.fgets() sobre scanf() o gets() para leer strings.fgets si hubo desborde, o usa strcspn para eliminar el salto de línea.char[] y char* para evitar errores de modificación de literales.<string.h> con precaución, especialmente strcpy y strcat que no verifican límites.