C - Malloc и memcpy (управление памятью) - PullRequest
7 голосов
/ 08 октября 2011

Я немного новичок в C и не могу понять, как работает память, особенно встроенные функции, такие как memcpy.

Вот struct Я использую

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

А вот вспомогательная функция, с которой я ее использую:

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;
}

Теперь в функции main у меня нет проблем с этим:

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

Но это вызывает ошибку сегмента:

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

Мой вопрос: почему? И как мне этого избежать? Пожалуйста, имейте в виду, что я новичок в C, поэтому то, как C взаимодействует с памятью, для меня немного ново

Спасибо.

Редактировать: это работает! Большое спасибо. Полностью пропустил указатель данных. Теперь все работает нормально, согласно Вальгринду.

Ответы [ 4 ]

7 голосов
/ 08 октября 2011

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

завершается неудачно, потому что data->data тип void * указывает на какой-то мусорный / недействительный адрес, который не выделен. data имеет адрес строкового литерала, который хранится в разделе только для чтения (как в .rodata исполняемого файла и загружен в память, что недоступно для записи. Кроме того, если вы этого не сделали присвойте такой строковый адрес переменной указателя, тогда он будет содержать какое-то недопустимое / мусорное значение адреса, которое не выделено или не инициализировано с каким-либо допустимым разрешенным расположением. Поэтому сначала выделите буфер.

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

malloc вернет первый адрес местоположения блока адреса по крайней мере size * sizeof (char) байтов. Теперь вы можете скопировать size байт в эту область памяти, указанную data->data.

Не забудьте освободить выделенный блок памяти после завершения работы с этим блоком памяти с помощью вызова free (addr).


Я вижу, вы пытались выделить буфер data очень странным образом (?):

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

, для которого дополнительно выделяется size байтов вместе с struct data_t. Но как бы то ни было, компонент data все еще указывает на какое-то место, которое нельзя изменить. Пожалуйста, используйте:

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

, чтобы бесплатно сначала сделать:

free (dt->data);

тогда

free (dt);
3 голосов
/ 08 октября 2011

Ваша первая ошибка заключается в следующем:

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

Это создаст кусок памяти размера struct data_t + size.Я думаю, что вы ожидали, что ваше поле данных внутри data_t может использовать эту память, но не может, потому что данные не содержат адрес этой памяти.

Ваша вторая ошибка заключалась в том, что вы копировали значениеследующая строка в «data»:

data->data="123456a";

Фактически, здесь произошло то, что в памяти есть строка «123456a», которая существует на протяжении всей жизни вашей программы.Когда вы назначаете «123456a» для data-> data, на самом деле происходит то, что вы берете адрес этой строки «123456a» и помещаете его в data-> data, вы копируете не значение («123456a»), а местоположениеили адрес (0x23822 ...) "123456a".

Ваша последняя ошибка была следующей:

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

Вы пытались скопировать значение "123456a" в память, указанную данными.На что указывают данные?Он указывает на область памяти, доступную только для чтения и содержащую ранее назначенную строку «123456a».Другими словами, вы сказали вашей программе писать по адресу «123456a».

Вот программа, которая будет делать то, что вы ожидаете:

#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;
}
1 голос
/ 08 октября 2011

Примечание void *data - указатель, в data_create вы не выделяете для него место, вы просто указываете на строковую константу "1234567890a", которая доступна только для чтения.

In main, вы создаете еще одну строковую константу "123456a", затем void *data указываете на строковую константу, которая доступна только для чтения.

Поэтому, когда вы вызываете memcpy для записи в адрес памяти, который не являетсяс возможностью записи (или без инициализации), вы получили ошибку.

0 голосов
/ 08 октября 2011

data-> data - указатель. И этот указатель указывает в никуда. Прежде чем делать memcpy, вы должны выделить место и сделать data-> data для указания на это пространство.

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

и тогда memcpy не будет работать до тех пор, пока data-> data! = NULL

делает

data->data = "123"

в порядке, потому что «123» выделяется во время компиляции, поэтому data-> data указывает на начало строки «123», но вызов memcpy(data->data,"123",4) не удастся, потому что указатель на data-data не инициализирован и указывает на некоторые случайное расположение в памяти, которое невозможно прочитать даже.

...