2010-06-28 13 views
37

Estoy tratando de entender cómo resolver este problema trivial en C, de la manera más limpia/más segura. Aquí está mi ejemplo:¿Cómo asignar correctamente un nuevo valor de cadena?

#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 works? 
    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; 
} 

de error del compilador es:

main.c: In function ‘main’: main.c:18: error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’

entiendo que C (no C++) no tiene ningún tipo String y en su lugar utiliza conjuntos de caracteres, por lo que otra manera de hacerlo era a alterar la estructura ejemplo para mantener los punteros de caracteres:

#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 hacer esto. Gracias.

+0

Si declaramos struct en 'main()', solo se puede acceder a la persona dentro de ella. Intenta moverte desde main para usarlo como global – EsmaeelE

Respuesta

29

El primer ejemplo no funciona porque no puede asignar valores a las matrices: las matrices funcionan (algo así como) los punteros const a este respecto. Lo que se puede hacer sin embargo es copiar un nuevo valor en la matriz:

strcpy(p.name, "Jane"); 

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

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

Actualización: ejemplo de buffers asignados dinámicamente (usando la definición struct 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); 
7

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

C tiene muy poco soporte sintáctico para cadenas. No hay operadores de cadenas (solo operadores de matriz de caracteres y operadores de caracteres). No puedes asignar cadenas.

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

La función strncpy() se puede usar aquí. Para una 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 echar un vistazo a las strncat() y memcpy() funciones.

+1

Por ahora la mejor respuesta pero también la de Péter es buena (mostrando 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. –

4

Las dos estructuras son diferentes. Cuando inicializa la primera estructura, se asignan unos 40 bytes de memoria. Cuando inicializa la segunda estructura, se asignan aproximadamente 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. Es por eso que

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

funciona en el primer ejemplo.

no se puede asignar (en el sentido convencional) una cadena en C

Los literales de cadena que tiene ("Juan") se cargan en memoria cuando el código se ejecuta. 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 la cadena. Hacer algo como:

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

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

+0

Tiene razón, la tarea que escribió causa una segfault porque está tratando de alterar un valor de puntero, pero refiriéndose 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 instrucción) que funciona como se esperaba. –

+1

Me gustaría mencionar que "alterar un valor de puntero" no necesariamente causa una segfault. El motivo por el cual el fragmento de mi publicación original provoca una segfault es porque está intentando modificar la memoria que se encuentra en un espacio de direcciones restringido. – Gus

Cuestiones relacionadas