Предположим, я хочу определить структуру, представляющую длину вектора и его значения как:
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.
Заранее спасибо.
c
memory-management
Pouya
источник
источник
malloc()
в C. Я никогда не пойму, почему все чувствуют необходимость в этом. :(void *
не выполняется автоматическое преобразование в другие указатели, и требуется приведение (или, конечно, не использоватьmalloc()
в C ++).Ответы:
Нет, вы не выделяете память
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); } }
Инкапсулируя создание таким образом, вы гарантируете, что векторы либо полностью построены, либо не построены вообще - нет никаких шансов, что они будут построены наполовину. Это также позволяет полностью изменить базовые структуры данных в будущем, не затрагивая клиентов (например, если вы хотите сделать их разреженными массивами, чтобы сэкономить место на скорости).
источник
Первый раз, вы выделить память для
Vector
, что означает , что переменныеx
,n
.Однако
x
пока не указывает ни на что полезное .Вот почему необходимо и второе распределение .
источник
Несколько очков
struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));
неправильноэто должно быть,
struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));
посколькуy
содержит указатель наstruct Vector
.1-й
malloc()
только выделяет память, достаточную для хранения векторной структуры (которая является указателем на double + int)2-й
malloc()
фактически выделяет память для хранения 10 двойных.источник
Фактически вы могли бы сделать это в одном malloc, выделив одновременно для вектора и массива. Например:
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.
источник
В принципе, вы уже делаете это правильно. Для того, что вы хотите, вам нужны две
malloc()
штуки.Просто несколько комментариев:
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));
источник
if(NULL == foo)
sizeof *y
. Если вы делаете это только для того, чтобы набрать на 5 букв меньше (sizeof * y против sizeof (Vector)) без других аргументов, то это должно быть потому, что вы считаете задачу набора 5 букв на клавиатуре серьезным препятствием. И если так, то, возможно, рассмотрите другой выбор карьеры, так как программирование включает в себя много набора текста с клавиатуры ...sizeof *y
помогает бороться с такими ошибками, как письмо,sizeof(Vector)
когда вы имели в видуsizeof(Matrix)
. Как часто вы допускаете подобные ошибки? Как быстро вы их находите и исправляете? Я согласен с тем, что это увеличивает безопасность типов, и я пишуsizeof *y
свой собственный код, но это почти так же параноидально, как и писать,if(NULL == foo)
чтобы предотвратить опечатки в==
.y
является указателем наstruct Vector
такsizeof *y
говорит «размер того , что у точек до», такsizeof struct Vector
.Когда вы выделяете память,
struct Vector
вы просто выделяете память для указателяx
, то есть для пространства, где будет помещено его значение, содержащее адрес. Таким образом, вы не выделяете память для блока, на которыйy.x
будет ссылаться.источник
Сначала malloc выделяет память для структуры, включая память для x (указатель на double). Второй malloc выделяет память для двойного значения, на которое указывает x.
источник
Когда вы
malloc(sizeof(struct_name))
автоматически выделяете память для полного размера структуры, вам не нужно распределять каждый элемент внутри.Используйте
-fsanitize=address
флаг, чтобы проверить, как вы использовали свою программную память.источник