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.
Спасибо заранее.
Ответы
Ответ 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".