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
Longitudse busca recorriendo hasta '\0'guardada, .size()
Tamañofijo al declararcrece y se encoge
Copiarstrcpy (puede desbordar)= (seguro)
Concatenarstrcat (puede desbordar)+=
Compararstrcmp (no ==)== funciona
Leer una líneacin.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?

Practícalo

Relacionado: tipos de datos (por qué char es un número pequeño), cin vs getline, vector vs array.