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:

  1. 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.
  2. strlen vs sizeof: strlen(miString)  dará 10 (los caracteres visibles).
    • sizeof(miString)  dará 11 (los 10 caracteres + el espacio oculto del \0).
  3. 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:

  1. Mira en la posición 0: ¿Es \0? No, es 'A'. Suma 1 al contador.
  2. Mira en la posición 1: ¿Es \0? No, es 'p'. Suma 1 al contador.
  3. ... así sucesivamente hasta que llega al final.
  4. 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:

  1. Verifica si el puntero está alineado en la memoria.
  2. Lee un bloque grande (por ejemplo, 64 bits).
  3. Usa una máscara de bits para detectar si dentro de ese bloque hay algún byte que sea \0.
  4. 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:

  1. 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' ...).
  2. Lógica de control: El bucle while sigue siendo nuestro mejor para recorrer el string hasta el terminador nulo \0.
  3. Los caracteres son números:  en C, un char es en realidad un número entero.
  4. 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

Entradas populares de este blog