Strings y carácteres
Strings y caracteres en C
En C sólo
tenemos memoria y bytes, no hay estructuras complejas predefinidas. En
particular no hay strings.
Tipos de
Datos Básicos en C
Los tipos
fundamentales se dividen principalmente en números enteros y números de punto
flotante.
|
Tipo |
Descripción |
Palabra Reservada |
|
Entero |
Números sin decimales (ej. 10, -5). |
int |
|
Carácter |
Un solo
carácter (ej. 'A', '9'). Realmente es un entero pequeño. |
char |
|
Flotante |
Números
con decimales de precisión simple. |
float |
|
Doble |
Números
con decimales de alta precisión. |
double |
|
|
|
|
¿Existe
el tipo string en C?
La
respuesta corta es no. No existe una palabra reservada string como en
Java, C# o Python.
En C, un
"string" es simplemente un arreglo (array) de caracteres que
termina con un carácter especial llamado nulo (\0). Este carácter le
indica a las funciones de C dónde termina el texto en la memoria.
Cómo trabajar con Strings
Como no es
un tipo de dato "nativo" con sus propios métodos, trabajar con ellos
requiere un poco más de cuidado manual.
1.
Declaración e Inicialización
- Como arreglo: char nombre[] =
"Hola"; (El compilador agrega el \0 automáticamente).
- Como puntero: char *saludo =
"Mundo"; (Apunta a una constante en memoria).
2. La
biblioteca <string.h>
Para
manipular strings, se importa esta librería. Aquí Las funciones más comunes:
- strlen(s): Devuelve la longitud del
string (sin contar el \0).
- strcpy(destino, origen): Copia el contenido de un
string a otro. No puedes usar el signo = para copiar arreglos.
- strcat(destino, origen): Concatena (une) el segundo
string al final del primero.
- strcmp(s1,
s2): Compara dos strings. Devuelve 0 si son iguales.
3.
Ejemplo rápido de Código C
#include <stdio.h>
#include <string.h>
int main()
{
char mensaje[20] = "Hola"; //
Espacio para 20 caracteres
strcat(mensaje, " C"); // Ahora dice "Hola C"
printf("%s\n", mensaje);
printf("Longitud: %d\n", strlen(mensaje));
return 0;
}
C no
verifica si te pasas del tamaño del arreglo. Si intentamos meter 50 caracteres
en un arreglo de 20, se podría sobrescribir otros datos y causar un error
crítico (buffer overflow).
CALCULAR
EL LARGO DE UN STRING
Para contar
los caracteres de un string en C, hay dos caminos: usar la función estándar
strlen() o recorrer el arreglo manualmente con un bucle hasta encontrar el
carácter nulo (\0).
Un ejemplo
que utiliza ambos métodos:
#include <stdio.h>
#include <string.h>
int main() {
char miString[] =
"Hola Mundo";
int contador = 0;
// Método 1: Usando la función de la
librería string.h
int longitud = strlen(miString);
// Método 2: Recorriendo el arreglo
manualmente
// Avanzamos mientras el carácter actual NO
sea el terminador nulo '\0'
while (miString[contador] != '\0') {
contador++;
}
printf("El
string es: %s\n", miString);
printf("Longitud (strlen): %d\n", longitud);
printf("Longitud (manual): %d\n",
contador);
return 0;
}
Conceptos
clave a recordar:
- El Terminador Nulo (\0): Como C no guarda el tamaño del
string en ningún sitio, funciones como strlen simplemente empiezan a
contar desde la posición 0 hasta que se topan con el byte 0.
- strlen vs sizeof: strlen(miString) dará 10 (los caracteres
visibles).
- sizeof(miString) dará 11 (los 10 caracteres + el
espacio oculto del \0).
- Librería: Se debe incluir #include
<string.h> si vas a usar las funciones integradas.
Vamos a
implementar la función largo, como función, y hacerlo manualmente es la mejor
forma de entender cómo C gestiona la memoria. Básicamente, necesitamos un
puntero o un índice que avance por el arreglo hasta "chocar" con el
muro invisible que es el carácter nulo \0.
#include
<stdio.h>
//
Definición de la función manual
int
calcularLargo(char cadena[]) {
int contador = 0;
// Mientras el carácter en la posición
'contador' no sea el nulo
while (cadena[contador] != '\0') {
contador++;
}
return contador;
}
int main()
{
char miTexto[] = "Aprender C es
genial";
int largo = calcularLargo(miTexto);
printf("El texto:
\"%s\"\n", miTexto);
printf("Tiene %d caracteres.\n",
largo);
return 0;
}
¿Qué
está pasando exactamente?
Cuando le
pasas miTexto a la función, C realmente está pasando la dirección de memoria
del primer carácter. La función entonces hace lo siguiente:
- Mira en la posición 0: ¿Es \0?
No, es 'A'. Suma 1 al contador.
- Mira en la posición 1: ¿Es \0?
No, es 'p'. Suma 1 al contador.
- ... así sucesivamente hasta que
llega al final.
- Al final de "genial",
el siguiente byte en memoria es el 0 binario. El bucle while
se rompe y devuelve el total.
Una
versión basada en punteros.
Si en vez
de usar índices [i], usamos aritmética de punteros…
int
calcularLargoPro(char *s) {
char *p = s;
while (*p != '\0') {
p++;
}
return p - s; // Restamos la posición final
menos la inicial
}
La
implementación exacta de la función strlen de la librería string.
Aunque cada
compilador (como GCC o Clang) puede tener su propia implementación optimizada,
siempre que respete el estándar (si quiere) la lógica es exactamente la que
vimos antes.
Lo que hay
"dentro" es un bucle que busca el byte cero.
La
implementación lógica básica
A nivel
conceptual, la función strlen se ve casi idéntica a la anterior:
size_t strlen(const char *s) {
const char *p = s;
while (*p != '\0')
{
p++;
}
return p - s;
}
Detalles importantes:
- size_t: No devuelve un int, sino un
size_t. Este es un tipo de dato especial en C diseñado específicamente
para representar tamaños de objetos en memoria. Es un entero sin
signo (nunca es negativo).
- const char *s: El uso de const es una
promesa de que la función no va a modificar el texto, solo lo va a
leer.
¿Cómo es
la strlen real?
En la
práctica, la función que viene con sistema operativo es mucho más rápida que un
simple bucle while.
Las
implementaciones modernas están escritas en lenguaje ensamblador o usan
trucos de aritmética de bits. En lugar de mirar carácter por carácter
(byte por byte), la función intenta leer bloques de 4 u 8 bytes a la vez
(palabras de memoria).
El proceso optimizado:
- Verifica si el puntero está
alineado en la memoria.
- Lee un bloque grande (por
ejemplo, 64 bits).
- Usa una máscara de bits para
detectar si dentro de ese bloque hay algún byte que sea \0.
- Si no hay \0, sigue con el
siguiente bloque. Esto hace que sea rápida para strings muy largos.
Una
advertencia sobre el rendimiento
Debido a
que strlen tiene que recorrer todo el string para saber cuánto mide, tiene una
complejidad de $O(n)$.
Atención
con un error común en su uso:
for (int i = 0; i < strlen(miString); i++) {
// Esto es muy lento porque strlen se ejecuta
// en cada vuelta del bucle.
}
Si el
string tiene 1,000 caracteres, el programa hará 1,000,000 de operaciones
innecesarias. Lo correcto es guardar el valor en una variable primero.
Hacer
una función en C que reciba un string y devuelva su cantidad de vocales.
Para contar
las vocales, el concepto es similar al de calcular el largo del string:
recorremos el arreglo carácter por carácter, pero esta vez añadimos una
condición para verificar si el carácter actual es una vocal ($a, e, i, o, u$),
ya sea en mayúscula o minúscula.
#include
<stdio.h>
#include
<ctype.h> // Necesaria para la función tolower
int
contarVocales(char cadena[]) {
int cantidad = 0;
int i = 0;
while (cadena[i]
!= '\0') {
// Convertimos el carácter a minúscula
para comparar más fácil
char c = tolower(cadena[i]);
if (c == 'a' || c == 'e' || c == 'i' ||
c == 'o' || c == 'u') {
cantidad++;
}
i++;
}
return cantidad;
}
int main()
{
char texto[] = "Hola, estoy
programando en C";
int totalVocales = contarVocales(texto);
printf("El texto:
\"%s\"\n", texto);
printf("Cantidad de vocales:
%d\n", totalVocales);
return 0;
}
Detalles
de la solución:
- tolower(): se ha incluido la librería
<ctype.h> para usar esta función. Convierte cualquier letra a
minúscula. Esto nos evita tener que escribir una condición gigante como if
(c == 'a' || c == 'A' || c == 'e' || c == 'E' ...).
- Lógica de control: El bucle while sigue siendo
nuestro mejor para recorrer el string hasta el terminador nulo \0.
- Los caracteres son números: en C, un char es en realidad un número
entero.
- No funciona para tildes.
Hacer
una función en C que dado un string devuelva su primer carácter.
Obtener el
primer carácter en C es una tarea muy sencilla porque un string es un arreglo.
El primer elemento siempre está en el índice 0.
#include
<stdio.h>
char
obtenerPrimerCaracter(char cadena[]) {
// Es buena práctica verificar si el string
no está vacío
if (cadena[0] != '\0') {
return
cadena[0];
} else {
// Si el string está vacío,
devolvemos el carácter nulo
return '\0';
}
}
int main() {
char miTexto[] = "Hola Mundo";
char primera =
obtenerPrimerCaracter(miTexto);
if (primera != '\0') {
printf("El primer carácter es:
%c\n", primera);
} else {
printf("El string está
vacío.\n");
}
return 0;
}
Asuntos interesantes:
- El índice [0]: En C, los arreglos son
"zero-indexed", lo que significa que el conteo siempre empieza
en cero.
- Seguridad: Si se intenta acceder a
cadena[0] en un string que solo tiene el terminador nulo (un string vacío
""), se obtiene el valor \0. Es importante validar esto para
evitar errores lógicos en programas más grandes.
- Punteros (Alternativa): Con punteros, se podría simplemente
hacer return *cadena;. Como el nombre del arreglo apunta a la primera
dirección de memoria, "desreferenciarlo" con el asterisco te da
el primer valor directamente.
Hacer
una función que reciba un string y devuelva
su último carácter.
Para
obtener el último carácter, necesitamos combinar dos conceptos que ya hemos
visto: encontrar el final del string (su longitud) y acceder a la posición
específica del arreglo.
#include <stdio.h>
#include <string.h>
char
obtenerUltimoCaracter(char cadena[]) {
// 1. Calculamos el largo del string
int largo = strlen(cadena);
// 2. Verificamos que el string no esté
vacío
if (largo > 0) {
// El último carácter está en la
posición largo - 1
// porque los índices empiezan en 0
return cadena[largo - 1];
} else {
return '\0'; // Devolvemos nulo si el
string está vacío
}
}
int main() {
char miTexto[] =
"Lenguaje C";
char ultimo = obtenerUltimoCaracter(miTexto);
if (ultimo != '\0') {
printf("El último carácter de
\"%s\" es: %c\n", miTexto, ultimo);
} else {
printf("El string está
vacío.\n");
}
return 0;
}
¿Por qué
usamos largo - 1?
Ejemplo el
string "Hola":
|
Índice |
0 |
1 |
2 |
3 |
4 |
|
Carácter |
'H' |
'o' |
'l' |
'a' |
'\0' |
- La función strlen nos dirá que
el largo es 4.
- Pero si intentas acceder a
cadena[4], estarás obteniendo el \0 (el terminador nulo).
- El carácter visible final ('a')
está en la posición 3.
Por eso, la
fórmula siempre es $posicion = largo - 1$.
Dado un número natural pero representado como un string de sus dígitos, pasarlo al tipo int, procesando digito a digito tal string. Si apareciera un caracter que no es digito devolver -1.
#include <stdio.h>
int stringToInt(char *str) {
int result = 0;
for (int i = 0; str[i] != '\0'; i++) {
char c = str[i];
// Verificar si el carácter es un dígito
if (c < '0' || c > '9') {
return -1; // No es dígito
}
// Convertir carácter a número y acumular
result = result * 10 + (c - '0');
}
return result;
}
int main() {
char ejemplo1[] = "12345";
char ejemplo2[] = "12a45";
printf("Resultado 1: %d\n", stringToInt(ejemplo1)); // 12345
printf("Resultado 2: %d\n", stringToInt(ejemplo2)); // -1
return 0;
}
Comentarios
Publicar un comentario