Подтвердить что ты не робот

Malloc для структуры и указателя в C

Предположим, что я хочу определить структуру, представляющую длину вектора, и его значения:

struct Vector{
    double* x;
    int n;
};

Теперь предположим, что я хочу определить вектор y и выделить для него память.

struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));

Мой поиск через интернет показывает, что я должен выделить память для x отдельно.

y->x = (double*)malloc(10*sizeof(double));

Но, кажется, я выделяю память для y- > x дважды, при распределении памяти для y и другой при распределении памяти для y- > x, и это кажется пустой тратой памяти. Очень приветствуем, если дайте мне знать, что делает компилятор и что будет правильным способом инициализируйте как y, так и y- > x.

Спасибо заранее.

4b9b3361

Ответ 1

Нет, вы не выделяете память для y->x дважды.

Вместо этого вы выделяете память для структуры (которая включает в себя указатель) плюс что-то для этого указателя, указывающего на.

Подумайте об этом так:

         1          2
        +-----+    +------+
y------>|  x------>|  *x  |
        |  n  |    +------+
        +-----+

Итак, вам действительно нужны два распределения (1 и 2) для хранения всего.

Кроме того, ваш тип должен быть struct Vector *y, поскольку он является указателем, и вы никогда не должны отбрасывать возвращаемое значение из malloc в C, так как оно может скрыть определенные проблемы, которые вы не хотите скрывать. C вполне способен неявно преобразование возвращаемого значения void* в любой другой указатель.

И, конечно, вы, вероятно, хотите инкапсулировать создание этих векторов, чтобы упростить управление ими, например:

struct Vector {
    double *data;    // no place for x and n in readable code :-)
    size_t size;
};

struct Vector *newVector (size_t sz) {
    // Try to allocate vector structure.

    struct Vector *retVal = malloc (sizeof (struct Vector));
    if (retval == NULL)
        return NULL;

    // Try to allocate vector data, free structure if fail.

    retVal->data = malloc (sz * sizeof (double));
    if (retVal->data == NULL) {
        free (retVal);
        return NULL;
    }

    // Set size and return.

    retVal->size = sz;
    return retVal;
}

void delVector (struct Vector *vector) {
    // Can safely assume vector is NULL or fully built.

    if (vector != NULL) {
        free (vector->data);
        free (vector);
    }
}

Инкапсулируя создание таким образом, вы гарантируете, что векторы либо полностью построены, либо вообще не созданы - нет никаких шансов, что они будут построены наполовину. Это также позволяет полностью изменить основные структуры данных в будущем, не затрагивая клиентов (например, если вы хотите сделать их разреженными массивами для компромиссного пространства для скорости).

Ответ 2

В первый раз вы выделяете память для Vector, что означает переменные x, n.

Однако x пока не указывает на что-либо полезное.

Таким образом, требуется второе выделение.

Ответ 3

Несколько точек

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector)); неверно

он должен быть struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));, так как y содержит указатель на struct Vector.

1st malloc() выделяет достаточно памяти, чтобы содержать векторную структуру (которая является указателем на double + int)

2nd malloc() фактически выделяет память для хранения 10 двойных.

Ответ 4

В принципе вы уже делаете это правильно. Для чего вам нужно два malloc() s.

Только некоторые комментарии:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));
y->x = (double*)malloc(10*sizeof(double));

должен быть

struct Vector *y = malloc(sizeof *y); /* Note the pointer */
y->x = calloc(10, sizeof *y->x);

В первой строке вы выделяете память для объекта Vector. malloc() возвращает указатель на выделенную память, поэтому y должен быть указателем вектора. Во второй строке вы выделяете память для массива из 10 удвоений.

В C вам не нужны явные приведения, а писать sizeof *y вместо sizeof(struct Vector) лучше для безопасности типов, и, кроме того, он сохраняет при вводе.

Вы можете изменить структуру и сделать один malloc() следующим образом:

struct Vector{    
    int n;
    double x[];
};
struct Vector *y = malloc(sizeof *y + 10 * sizeof(double));

Ответ 5

При распределении памяти для struct Vector вы просто выделяете память для указателя x, т.е. для пробела, где будет размещено его значение, содержащее адрес. Таким образом, вы не выделяете память для блока, на котором будет ссылаться y.x.

Ответ 6

Первый malloc выделяет память для struct, включая память для x (указатель на double). Второй malloc выделяет память для двойного значения с x точками.

Ответ 7

Вы могли бы сделать это в одном malloc путем выделения для Vector и массива одновременно. Например:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector) + 10*sizeof(double));
y->x = (double*)((char*)y + sizeof(struct Vector));
y->n = 10;

Это выделяет Vector 'y', затем y- > x указывает на дополнительные распределенные данные сразу после структуры Vector (но в том же блоке памяти).

Если изменение размера вектора требуется, вы должны сделать это с двумя распределениями, как рекомендовано. Тогда внутренняя матрица y- > x могла бы быть изменена, сохраняя при этом векторную структуру "y".