Menu

Funciones de cadenas en C: strlen, strcpy, strcat, strcmp y más

Un recorrido práctico por <string.h>: medir con strlen, copiar con strcpy y strncpy, unir con strcat, comparar con strcmp, buscar con strchr y strstr, y cómo dimensionar los búferes para que ninguna se desborde.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Cada función de <string.h> es un pequeño bucle sobre un array de char que se detiene en el terminador nulo. En cuanto eso queda claro, la biblioteca deja de parecer una lista de nombres crípticos y empieza a parecer el puñado de bucles que habrías escrito tú. Esta página cubre las que cargan con casi todo el trabajo real, y las reglas de tamaño que evitan que escriban más allá del final de tus búferes.

Todo lo de aquí necesita una inclusión:

#include <string.h>

strlen: cuántos caracteres

strlen cuenta los bytes hasta el terminador, sin incluirlo. Devuelve size_t, así que imprímelo con %zu.

strlen da 6; sizeof da 32. La primera es el texto, la segunda es el almacenamiento; mira cadenas para entender por qué esa distinción importa tanto.

strlen recorre la cadena entera en cada llamada, así que no la pongas en la condición de un bucle sobre la misma cadena inmutable:

/* vuelve a contar la cadena entera en cada iteracion */
for (size_t i = 0; i < strlen(s); i++) { ... }

/* cuenta una vez */
size_t n = strlen(s);
for (size_t i = 0; i < n; i++) { ... }

strcpy y strncpy: copiar

strcpy(dst, src) copia los caracteres más el terminador. No tiene idea de cuán grande es dst, así que quien llama debe garantizar que dst tenga al menos strlen(src) + 1 bytes.

strncpy toma un conteo máximo, pero tiene una trampa famosa: cuando el origen mide al menos n bytes copia exactamente n caracteres y ningún terminador. Termina siempre tú mismo:

El sizeof dst - 1 más el terminador explícito es el patrón que hay que memorizar. (strncpy además rellena con ceros los orígenes cortos hasta n bytes, lo que es trabajo desperdiciado en búferes grandes: se diseñó para registros de ancho fijo, no por seguridad.)

strcat y strncat: unir

strcat(dst, src) añade src al final de lo que ya haya en dst. El destino debe poder contener ambas cadenas más un terminador, y ya debe contener una cadena válida: añadir a memoria sin inicializar es comportamiento indefinido.

Fíjate en char path[64] = "/home/ada"; en vez de char path[64];: el inicializador es lo que la convierte en una cadena válida para el primer strcat.

strncat(dst, src, n) añade como mucho n caracteres y siempre agrega un terminador, así que n es el espacio restante, no el tamaño total del búfer:

strncat(dst, src, sizeof dst - strlen(dst) - 1);

Las llamadas repetidas a strcat vuelven a recorrer el destino para encontrar su final cada vez. Construir así una cadena larga en un bucle es cuadrático; para cualquier cosa de tamaño considerable, lleva tú la posición de escritura o usa snprintf, que se cubre en conversión de cadenas.

strcmp: comparar

== compara direcciones, así que es falso para dos arrays distintos que contienen el mismo texto. strcmp compara los caracteres y devuelve el signo de la diferencia.

Tres reglas:

  • Comprueba strcmp(a, b) == 0 para la igualdad. Escribir if (strcmp(a, b)) significa "si difieren", que casi todo el mundo lee al revés.
  • Solo el signo está definido. No compares el resultado con 1 ni con -1.
  • El orden es por valor de byte, así que "Zebra" va antes que "manzana" en ASCII. Para comparar sin distinguir mayúsculas, pasa antes ambas copias a minúsculas: la muy vista strcasecmp es una extensión POSIX, no C estándar.

strncmp(a, b, n) compara solo los primeros n caracteres, que es la forma limpia de comprobar un prefijo:

strchr y strstr: buscar

strchr(s, ch) encuentra la primera aparición de un carácter; strrchr encuentra la última. strstr(haystack, needle) encuentra una subcadena. Las tres devuelven un puntero dentro de la cadena original, o NULL cuando no hay coincidencia.

Restar el puntero devuelto al inicio da el índice; su tipo es ptrdiff_t, que se imprime con %td. Como el resultado apunta dentro del array original, at + 1 es el resto de la cadena sin copiar nada en absoluto: un modismo muy común en C.

Comprueba siempre si es NULL antes de desreferenciar. strchr(email, '@') + 1 sobre una cadena sin @ calcula una dirección a partir de NULL y el programa deja de estar definido.

Partir una cadena

No hay ninguna función de división, pero strchr más un terminador hacen el trabajo en el sitio. Este ejemplo corta un correo en usuario y dominio:

memcpy copia una cantidad exacta de bytes sin preocuparse por los terminadores, que es lo que quieres cuando ya conoces la longitud. El terminador se escribe después a mano.

Un ejemplo resuelto: normalizar un nombre

Juntando el recorrido: recortar, comparar, copiar y unir.

Cada escritura está acotada por el propio sizeof del destino, y cada strncpy va seguido de un terminador explícito. Esa disciplina, aplicada de forma consistente, es lo que hace seguro el código de cadenas en C.

Reglas de tamaño que conviene guardar

  • strcpy necesita strlen(src) + 1 bytes libres en el destino.
  • strcat necesita strlen(dst) + strlen(src) + 1.
  • Tras un strncpy, escribe tú dst[n - 1] = '\0'.
  • Para strncat, el conteo es el espacio que queda: sizeof dst - strlen(dst) - 1.
  • Comprueba si strchr y strstr dieron NULL antes de usar el resultado.
  • Usa sizeof dst solo cuando dst sea un array real. Dentro de una función que recibe char *dst, sizeof da el tamaño del puntero: pasa la longitud del búfer como un parámetro aparte.

Preguntas frecuentes

¿Qué devuelve strcmp en C?

Cero cuando las dos cadenas son idénticas, un valor negativo cuando la primera va antes que la segunda y un valor positivo cuando va después. Solo el signo es significativo: nunca supongas que devuelve -1 o 1. La prueba que quieres es casi siempre if (strcmp(a, b) == 0).

¿Por qué no puedo comparar cadenas con == en C?

a == b compara las dos direcciones, no los caracteres. Dos arrays distintos que contienen "gato" viven en direcciones distintas, así que la comparación es falsa aunque el texto coincida. Usa strcmp(a, b) == 0 de <string.h>.

¿Cuál es la diferencia entre strcpy y strncpy?

strcpy copia hasta el '\0' del origen sin idea de cuán grande es el destino. strncpy se detiene tras n bytes como mucho, pero si el origen es así de largo no copia terminador, así que debes escribir dst[n - 1] = '\0'; tú mismo. Ninguna es automáticamente segura; la disciplina del tamaño es tuya.

¿Cómo busco una subcadena en C?

strstr(haystack, needle) devuelve un puntero a la primera aparición, o NULL si no hay ninguna. Como devuelve un puntero dentro de la cadena original, found - haystack te da el índice. Usa strchr cuando busques un solo carácter.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR