¿Cómo puedo asignar correctamente un nuevo valor de cadena?

5 minutos de lectura

avatar de usuario
Gianluca Bargelli

Estoy tratando de entender cómo resolver este problema trivial en C, de la manera más limpia/segura. Aquí está mi ejemplo:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;

    // Here I can pass strings as values...how does it work?
    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    // This works as expected...
    p.age = 25;

    //...but the same approach doesn't work with a string
    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

El error del compilador es:

main.c: En la función ‘main’: main.c:18: error: tipos incompatibles al asignar al tipo ‘char[20]’ del tipo ‘char *’

Entiendo que C (no C++) no tiene un tipo String y en su lugar usa matrices de chars, por lo que otra forma de hacer esto fue alterar la estructura de ejemplo para contener punteros de chars:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char *name;
        char *surname;
        int unsigned age;
    } person;

    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    p.age = 25;

    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

Esto funciona como se esperaba, pero me pregunto si hay una mejor manera de hacerlo.

avatar de usuario
Péter Török

El primer ejemplo no funciona porque no puede asignar valores a las matrices: las matrices funcionan (más o menos) como punteros constantes a este respecto. Sin embargo, lo que puede hacer es copiar un nuevo valor en la matriz:

strcpy(p.name, "Jane");

Las matrices de caracteres están bien para usar si conoce el tamaño máximo de la cadena de antemano, por ejemplo, en el primer ejemplo, está 100% seguro de que el nombre cabe en 19 caracteres (no 20 porque siempre se necesita un carácter para almacenar el cero final valor).

Por el contrario, los punteros son mejores si no conoce el tamaño máximo posible de su cadena y/o si desea optimizar el uso de la memoria, por ejemplo, evite reservar 512 caracteres para el nombre “John”. Sin embargo, con los punteros, debe asignar dinámicamente el búfer al que apuntan y liberarlo cuando ya no se necesite, para evitar pérdidas de memoria.

Actualizar: ejemplo de búferes asignados dinámicamente (usando la definición de estructura en su segundo ejemplo):

char* firstName = "Johnnie";
char* surname = "B. Goode";
person p;

p.name = malloc(strlen(firstName) + 1);
p.surname = malloc(strlen(surname) + 1);

p.age = 25;
strcpy(p.name, firstName);
strcpy(p.surname, surname);

printf("Name: %s; Age: %d\n",p.name,p.age);

free(p.surname);
free(p.name);

Piense en las cadenas como objetos abstractos y en las matrices de caracteres como contenedores. La cadena puede tener cualquier tamaño, pero el contenedor debe tener al menos 1 más que la longitud de la cadena (para contener el terminador nulo).

C tiene muy poco soporte sintáctico para cadenas. No hay operadores de cadenas (solo operadores char-array y char-pointer). No puede asignar cadenas.

Pero puede llamar a funciones para ayudar a lograr lo que desea.

Él strncpy() La función podría usarse aquí. Para máxima seguridad sugiero seguir este patrón:

strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case

También eche un vistazo a la strncat() y memcpy() funciones

  • Por ahora, la mejor respuesta, pero también la de Péter es buena (muestra cómo hacerlo con punteros), así que estoy esperando un poco más para ver si más personas pueden agregar más consejos/sugerencias sobre el tema.

    – Gianluca Bargelli

    28 de junio de 2010 a las 10:38


  • Unos años tarde. Si hacemos algo como char *s; s = "foobar"; que, a diferencia de las matrices, realmente funciona. quiero decir que no puedo hacer char s[9]; s = "foobar". ¿No estamos tratando de asignar un valor de cadena a s ¿en ambos casos?

    – mayankkaizen

    7 de mayo de 2020 a las 9:42


Las dos estructuras son diferentes. Cuando inicializa la primera estructura, se asignan alrededor de 40 bytes de memoria. Cuando inicializa la segunda estructura, se asignan unos 10 bytes de memoria. (La cantidad real depende de la arquitectura)

Puede usar los literales de cadena (constantes de cadena) para inicializar matrices de caracteres. Esta es la razón por

persona p = {“Juan”, “Doe”,30};

funciona en el primer ejemplo.

No puede asignar (en el sentido convencional) una cadena en C.

Los literales de cadena que tiene (“John”) se cargan en la memoria cuando se ejecuta su código. Cuando inicializa una matriz con uno de estos literales, la cadena se copia en una nueva ubicación de memoria. En su segundo ejemplo, simplemente está copiando el puntero a (ubicación de) el literal de cadena. Haciendo algo como:

char* string = "Hello";
*string = 'C'

podría causar errores de compilación o tiempo de ejecución (no estoy seguro). Es una mala idea porque está modificando la cadena literal “Hola” que, por ejemplo, en un microcontrolador, podría ubicarse en la memoria de solo lectura.

  • Tiene razón, la tarea que escribió causa un error de segmento porque está tratando de alterar el valor de un puntero, pero en referencia a mi ejemplo, las cosas son como char *string = “Hello”; cadena = “C”; (tenga en cuenta que no hay asignación de puntero en la última declaración) que funciona como se esperaba.

    – Gianluca Bargelli

    28 de junio de 2010 a las 10:07

  • Me gustaría mencionar que “alterar el valor de un puntero” no necesariamente causa una falla de segmento. La razón por la que el fragmento en mi publicación original provoca un error de segmento es porque está tratando de modificar la memoria que se encuentra en un espacio de direcciones restringido.

    – Gus

    28 de junio de 2010 a las 10:13


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad