char[] vs std::string: qué es de verdad una cadena C
Respuesta corta: una cadena estilo C es simplemente un array de char con un byte extra al final que contiene '\0'. Ese único convenio explica por qué existe strlen, por qué strcpy puede desbordar y por qué se inventó std::string.
Toda la historia en un dibujo
char s[6] = "hi"; // 6 huecos, pero solo 3 significan algo
índice: 0 1 2 3 4 5
+-----+-----+-----+-----+-----+-----+
valor: | 'h' | 'i' | '\0'| ? | ? | ? |
+-----+-----+-----+-----+-----+-----+
^
el terminador: así sabe toda función C dónde acaba el texto
Los tres últimos huecos son basura sin inicializar; la cadena termina donde dice el '\0'. Nadie guarda una longitud.
Crear e imprimir
#include <iostream>
using namespace std;
int main() {
char s[20] = "hi";
s[0] = 'H'; // indexa como en cualquier array
cout << s << endl; // Hi
char vacio[20]; // sin inicializar: ¡todavía sin terminador!
vacio[0] = 'o';
vacio[1] = 'k';
vacio[2] = '\0'; // ahora sí es una cadena válida
cout << vacio << endl;
return 0;
}
Olvidar el terminador es el bug número uno de las cadenas C: imprimir un array sin él recorre memoria más allá de tus datos hasta que da con un byte cero por casualidad.
strlen, strcpy — y por qué existen
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char src[20] = "copy me";
char dst[20];
strcpy(dst, src); // copia, incluido el '\0'
cout << dst << endl;
cout << "longitud = " << strlen(dst) << endl; // 7, el terminador no cuenta
cout << "sizeof dst = " << sizeof(dst) << endl; // 20: tamaño del array, no del texto
return 0;
}
El par strlen (7) y sizeof (20) es una trampa clásica de entrevista: uno mide el texto, el otro la caja.
Por qué strcpy es peligroso
char pequeno[5];
char grande[] = "esto es mucho más largo que cinco";
strcpy(pequeno, grande); // escribe más allá del final — comportamiento indefinido
strcpy copia hasta ver '\0' y no sabe cuánto mide tu array. Eso es un desbordamiento de búfer, y es el origen de una parte enorme de los bugs de seguridad de las últimas décadas. La versión con límite es strncpy (o mejor: no uses cadenas C).
Qué cambia std::string
#include <string>
std::string s = "hi";
s[0] = 'H'; // sigue siendo indexable
s += " there"; // crece — sin tamaño que gestionar
std::cout << s.size(); // siempre sabe su propia longitud
std::cout << s.substr(0, 2); // operaciones en vez de convenios
std::string es dueño de su memoria, crece automáticamente y lleva su longitud consigo. No hay terminador que olvidar, no hay desbordamiento por un búfer fijo, no hay que llamar a strlen para encontrar el final.
Lado a lado
char[] (cadena C) | std::string | |
|---|---|---|
| Longitud | se busca recorriendo hasta '\0' | guardada, .size() |
| Tamaño | fijo al declarar | crece y se encoge |
| Copiar | strcpy (puede desbordar) | = (seguro) |
| Concatenar | strcat (puede desbordar) | += |
| Comparar | strcmp (no ==) | == funciona |
| Leer una línea | cin.getline(s, n) | getline(cin, s) |
La trampa que muerde a los principiantes
#include <iostream>
using namespace std;
int main() {
char a[10] = "abc";
char b[10] = "abc";
// Esto compara *direcciones*, no texto. ¡Siempre falso (o peor)!
cout << "a == b ? " << (a == b) << endl;
// Comparar carácter a carácter es lo que de verdad quieres:
bool iguales = true;
for (int i = 0; a[i] != '\0' || b[i] != '\0'; i++) {
if (a[i] != b[i]) { iguales = false; break; }
}
cout << "¿mismo texto? " << iguales << endl;
return 0;
}
Con std::string, a == b hace lo correcto. Con arrays, == compara la dirección del array consigo misma — casi siempre la pregunta equivocada.
Leer entrada con espacios
#include <iostream>
using namespace std;
int main() {
char linea[50];
cin.getline(linea, 50); // línea completa, espacios incluidos
cout << "[" << linea << "]" << endl;
return 0;
}
Recuerda que cin >> s se detiene en el primer espacio. getline toma el resto de la línea — y cuidado con el salto de línea que queda si mezclas los dos (ver cin vs getline).
¿Cuál deberías usar?
- Aprender o resolver problemas de jueces: muchos correctores automáticos y libros antiguos usan cadenas C. Conócelas — y conoce sus límites.
- Cualquier código C++ real:
std::string. Es más seguro, crece y se lee mejor. - Hablar con bibliotecas C:
std::string::c_str()cuando una función exigeconst char*.
Practícalo
- Invertir una cadena — indexa hacia atrás con
strlen. - Contar vocales — recorre el array hasta
'\0'. - Número palíndromo — el primo numérico de invertir una cadena.
Relacionado: tipos de datos (por qué char es un número pequeño), cin vs getline, vector vs array.