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


84

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

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

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

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

Заранее спасибо.


5
Как в высшей степени подчеркнуто paxdiablo, пожалуйста, не переводите возвращаемое значение malloc()в C. Я никогда не пойму, почему все чувствуют необходимость в этом. :(
раскрутите

14
@unwind, может быть, они старые программисты на C ++, переходящие на C :-)
paxdiablo

@unwind При использовании компилятора Nvidia nvcc в коде C, если я не приведу результат malloc, он выдаст ошибку.
Nubcake

@Nubcake Согласно этой ссылке, это может быть связано с тем, что nvcc запускает базовый компилятор в режиме C ++ из-за того, что их интерфейс CUDA является C ++. В C вы не получите для этого ошибок. В C ++ void *не выполняется автоматическое преобразование в другие указатели, и требуется приведение (или, конечно, не использовать malloc()в C ++).
расслабьтесь

@unwind Ага, позже я об этом узнал :) Просто хотел изложить ситуацию, когда, если вы не приведете результат, будет выдана ошибка.
Nubcake

Ответы:


156

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

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

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

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

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

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

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


28
Трепещите в моем ASCII арт skillage :-)
paxdiablo

Спасибо большое. Действительно полезно.
Pouya

1
В if (retval == NULL) retval должен быть retVal
Legion Daeth

5

Первый раз, вы выделить память для 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.

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

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


2

Фактически вы могли бы сделать это в одном malloc, выделив одновременно для вектора и массива. Например:

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

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


Почему вы ввели преобразование y в char? Почему бы не (double *) y + sizeof (struct Vector)?
Вишну Прасат

sizeof возвращает размер структуры в байтах, а арифметический оператор указателя '+' добавит к указателю 'y' количество, кратное sizeof ( y). Если бы мы сделали то же, что и выше, y было бы увеличено на sizeof (double) * sizeof (struct), что слишком много. Преобразование y в char позволяет нам увеличивать y на sizeof (char) * sizeof (struct) = 1 * sizeof (struct)
PQuinn

2

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

Просто несколько комментариев:

должно быть

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

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

Вы можете переупорядочить свою структуру и сделать malloc()так:


«Экономия на печатании» никогда не является веским аргументом в пользу программных решений. Настоящая причина, по которой вы выбрали * y, - это соображения безопасности, гарантирующие, что вы выделите столько места, сколько необходимо для соответствующей переменной.
Lundin

@Lundin Я обновил свой ответ, но для меня аргумент «безопасность типов» почти в той же лиге, что и письмоif(NULL == foo)
Вернси

Вы тот, кто проповедует использовать sizeof *y. Если вы делаете это только для того, чтобы набрать на 5 букв меньше (sizeof * y против sizeof (Vector)) без других аргументов, то это должно быть потому, что вы считаете задачу набора 5 букв на клавиатуре серьезным препятствием. И если так, то, возможно, рассмотрите другой выбор карьеры, так как программирование включает в себя много набора текста с клавиатуры ...
Лундин

@Lundin Writing sizeof *yпомогает бороться с такими ошибками, как письмо, sizeof(Vector)когда вы имели в виду sizeof(Matrix). Как часто вы допускаете подобные ошибки? Как быстро вы их находите и исправляете? Я согласен с тем, что это увеличивает безопасность типов, и я пишу sizeof *yсвой собственный код, но это почти так же параноидально, как и писать, if(NULL == foo)чтобы предотвратить опечатки в ==.
Вернси

1
@ShmuelKamensky Функционально они эквивалентны. yявляется указателем на struct Vectorтак sizeof *yговорит «размер того , что у точек до», так sizeof struct Vector.
Вернси,

1

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


1

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


0

Когда вы malloc(sizeof(struct_name))автоматически выделяете память для полного размера структуры, вам не нужно распределять каждый элемент внутри.

Используйте -fsanitize=addressфлаг, чтобы проверить, как вы использовали свою программную память.


Неправильно. x - это просто указатель, вам нужно выделить память для значения, на которое указывает x. Прочтите другой ответ, чтобы получить более подробную информацию. (например: stackoverflow.com/a/14768280/5441253 )
Максим Ашуров
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.