2011-10-08 20 views
7

Soy un poco nuevo en C y tengo problemas para entender cómo funciona la memoria, especialmente las funciones integradas como memcpy.C - Malloc y memcpy (gestión de memoria)

Aquí hay una struct estoy usando

struct data_t { 
    int datasize; 
    void *data; 
}; 

Y aquí es una función auxiliar que lo estoy usando con:

struct data_t *data_create(int size) 
{ 
    struct data_t *dt=malloc(sizeof(struct data_t)+size); 
    dt->datasize=size; 
    dt->data="1234567890a"; 
    return dt; 
} 

Ahora en la función main tengo ningún problema en hacer esto:

struct data_t *data = data_create(1024); 
data->data="123456a";//just an example 

Pero esto arroja una falla Seg:

memcpy(data->data,"123456a",strlen("1234567890a")+1); 

Mi pregunta es ¿por qué? ¿Y cómo lo evito? Tenga en cuenta que soy nuevo en C, por lo que C trata de la memoria es un poco nuevo para mí

Gracias.

Editar: ¡Funciona! Muchas gracias. Completamente perdido el puntero de datos. Ahora todo está funcionando bien según valgrind.

Respuesta

6

memcpy(data->data,"123456a",strlen("1234567890a")+1);

falla porque data->data un void * puntos de tipo a alguna dirección de la basura/inválido, que no hayan sido atribuidas. data tiene la dirección del literal de cadena que se almacena en el sección de solo lectura (como en el .rodata del archivo ejecutable y se carga en la memoria, que no se puede escribir. Además, si no asignó dicha dirección de cadena al puntero variable, que llevaría a cabo un cierto valor de dirección no válida/basura que no se asigna o se ha inicializado con un poco de emplazamiento autorizado válido. Así que primero asignar el almacenamiento intermedio.

data->data = malloc (sizeof (char) * size); 

el malloc volverá la primera dirección de ubicación de un bloque de direcciones por lo menos size * sizeof (char) bytes. Ahora puede copiar size bytes en esta ubicación de memoria apuntada por data->data.

Recuerde liberar el bloque de memoria asignado cuando haya terminado de trabajar con esa memoria bock con la llamada free (addr).


Veo que ha intentado asignar data tampón con una forma muy extraña (?):

struct data_t *dt=malloc(sizeof(struct data_t)+size); 

para los que el extra asignados size bytes junto con el struct data_t. Pero cualquiera que sea el caso, el componente data aún apunta a algún lugar que no se puede cambiar. por favor use:

struct data_t *dt = malloc(sizeof(struct data_t)); 
dt->data = malloc (sizeof (char) * size); 
memcpy (data->data, "whatever", sizeof ("whatever")+1); 
return dt; 

para liberar primero hacer:

free (dt->data); 

continuación

free (dt); 
+0

espacio se asignó en la asignación inicial; el puntero simplemente no estaba configurado para señalar el espacio asignado. –

+0

@JonathanLeffler: sí, el asker asignó un poco de espacio adicional al asignar inicialmente, debería haberse inicializado al punto. Seguiré prefiriendo la llamada explícita, si no hay una necesidad específica de asignar todo de una vez. – phoxis

+0

Esto dará un error: "conversión inválida de 'void *' a 'data_t *' [-fpermissive]" para resolver "tiene que lanzar explícitamente el puntero devuelto por malloc". – VasaraBharat

1

Aviso void *data es un puntero, en data_create, usted no asigna espacio para ello, simplemente haz que apunte a una constante de cadena "1234567890a" que es de solo lectura.

En main, crea otra constante de cadena "123456a", luego hace void *data apuntando a la constante de cadena, que es de solo lectura.

Por lo tanto, cuando llame al memcpy para escribir en una dirección de memoria que no se puede escribir (o sin inicializar), aparece un error.

0

data-> data is puntero. Y este puntero apunta a ninguna parte. Antes de hacer memcpy, debe asignar espacio y hacer data-> data para apuntar en este espacio.

data->data = malloc(strlen("1234567890a")+1); 

y luego establecimiento de memoria no fallará siempre que Datos-> datos! = NULL

haciendo

data->data = "123"

es aceptable, ya que "123" se asigna en tiempo de compilación, entonces los datos-> apuntan al comienzo de la cadena "123", pero la llamada al memcpy(data->data,"123",4) fallará, porque el puntero a los datos de datos no está inicializado y apunta a una ubicación aleatoria en la memoria que ni siquiera se puede leer.

+0

El 'memcpy' aún no es válido si intenta copiar más bytes de la fuente que la fuente, que es el caso aquí. – Mat

+0

Sí, hay dos errores en su código –

3

Su primer error es el siguiente:

struct data_t *dt=malloc(sizeof(struct data_t)+size); 

Esto creará un trozo de memoria del tamaño de la estructura data_t + tamaño. Creo que lo que esperabas era que tu campo de datos dentro de data_t pudiera usar esta memoria, pero no puede porque los datos no contienen una dirección en esta memoria.

Su segundo error fue suponer que donde copiar el valor de la siguiente cadena en "datos":

data->data="123456a"; 

Lo que en realidad ha pasado aquí es que hay una cadena en la memoria "123456a" que existe durante toda la vida de tu programa. Cuando asigna "123456a" a data-> data, lo que realmente está sucediendo es que está tomando la dirección de esta cadena "123456a" y poniéndola en data-> data, no está copiando el valor ("123456a") sino la ubicación o dirección (0x23822 ...) de "123456a".

Tu error final fue la siguiente:

memcpy(data->data,"123456a",strlen("1234567890a")+1); 

Ha intentado copiar el valor "123456a" en la memoria apuntada por los datos. ¿A qué apuntan los datos? Está apuntando a un área de memoria de solo lectura que contiene la cadena "123456a" previamente asignada. En otras palabras, le dijo a su programa que escriba a la dirección de "123456a".

Aquí es un programa de lo que va a hacer lo que espera:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <assert.h> 

typedef struct { 
    size_t datasize; 
    char *data; 
} data_t; 

data_t *data_create(size_t size) 
{ 
    data_t *dt; 

    dt = malloc(sizeof(data_t)); 
    assert(dt != NULL); 
    dt->data = malloc(size); 
    assert(dt->data != NULL); 
    dt->datasize = size; 

    /* need to decide what to do when this happens */ 
    assert((strlen("1234567890a") + 1) < size); 
    strcpy(dt->data, "1234567890a"); 

    return dt; 
} 

void data_destroy(data_t *dt) 
{ 
    free(dt->data); 
    free(dt); 
} 

int main(void) 
{ 
    data_t *data = data_create(1024); 
    /* data->data="123456a"; DONT DO THIS YOU WILL CAUSE A MEMORY LEAK */ 

    assert(data->datasize >= (strlen("123456a")+1)); 
    memcpy(data->data, "123456a", strlen("123456a")+1); 

    printf("%s\n", data->data); 

    data_destroy(data); 

    return EXIT_SUCCESS; 
}