Разница в создании структуры с использованием mallo c и без malloc - PullRequest
1 голос
/ 02 февраля 2020

Может кто-нибудь объяснить мне разницу между созданием структуры с и без mallo c. Когда следует использовать mallo c и когда следует использовать обычную инициализацию?

Например:

struct person {

    char* name;

};

struct person p = {.name="apple"};

struct person* p_tr = malloc(sizeof(struct person));
p_tr->name = "apple";

Какая разница между ними? Когда один подход будет использоваться над другими?

Ответы [ 5 ]

1 голос
/ 02 февраля 2020

Если мы предположим, что оба примера происходят внутри функции, то в:

struct person p = {.name="apple"};

реализация C автоматически выделяет память для p и освобождает ее, когда заканчивается выполнение функции (или, если оператор находится внутри блока, вложенного в функцию, когда заканчивается выполнение этого блока). Это полезно, когда:

  • Вы работаете с объектами небольшого размера. (Для больших объектов, использующих много кибибайтов памяти, malloc может быть лучше. Пороги варьируются в зависимости от обстоятельств.)
  • Вы работаете с небольшим количеством объектов одновременно.

In:

struct person* p_tr = malloc(sizeof(struct person));
p_tr->name = "apple";

программа явно запрашивает память для объекта, и программа, как правило, должна освободить эту память с free, когда это будет сделано с объектом. Это полезно, когда:

  • Объект должен быть возвращен вызывающей функции. Автоматический c объект, как использовано выше, перестанет существовать (в модели вычислений C; реальная память на вашем компьютере не перестает существовать, а просто больше не зарезервирована для использования объектом), когда выполнение функции заканчивается, но этот выделенный объект будет продолжать существовать, пока программа не освободит его (или не завершит выполнение).
  • Объект очень большой. (Как правило, реализации C предоставляют больше памяти для выделения malloc, чем для автоматических c объектов.)
  • Программа создаст переменное количество таких объектов в зависимости от обстоятельств, таких как создание связанных списков, деревьев или других структур из входных данных, размер которых неизвестен до его чтения.

Обратите внимание, что struct person p = {.name="apple"}; инициализирует элемент name с "apple" и инициализирует все остальные элементы в ноль. Однако код, который использует malloc и присваивает p_tr->name, не инициализирует другие члены.

Если struct person p = {.name="apple"}; появляется вне функции, то он создает объект со сроком хранения c , Он будет существовать во время выполнения программы.

Вместо struct person* p_tr = malloc(sizeof(struct person)); предпочтительно использовать struct person *p_tr = malloc(sizeof *p_tr);. В первом случае изменение p_tr требует редактирования в двух местах, что позволяет человеку совершать ошибки. С последним, изменение типа p_tr только в одном месте все равно приведет к запрашиваемому правильному размеру.

1 голос
/ 02 февраля 2020

Имея структуру данных типа;

struct myStruct {
    int a;
    char *b;
};

struct myStruct p;  // alternative 1
struct myStruct *q = malloc(sizeof(struct myStruct));  // alternative 2
  • Альтернатива 1: выделяет myStruct ширину области памяти в стеке и возвращает вам адрес памяти структуры (т. е. &p дает вам первый байтовый адрес структуры). Если она объявлена ​​в функции, ее срок действия заканчивается при выходе из функции (т. Е. Если функция выходит из области видимости, вы не можете ее достичь).

  • Альтернатива 2: выделяет myStruct ширина пространства памяти в heap и ширина указателя пространства памяти типа (struct myStruct*) в stack . Значение указателя в стеке получает значение адреса памяти структуры (которая находится в куче), и этот адрес указателя (не фактический адрес struct s) возвращается вам. Это время жизни никогда не заканчивается, пока вы не используете free(q).

В последнем случае, скажем, myStruct находится по адресу памяти 0xabcd0000, а q - по адресу памяти 0xdddd0000; затем значение указателя на адрес памяти 0xdddd0000 назначается как 0xabcd0000, и оно возвращается вам.

printf("%p\n", &p); // will print "0xabcd0000" (the address of struct)

printf("%p\n", q);  // will print "0xabcd0000" (the address of struct)
printf("%p\n", &q); // will print "0xdddd0000" (the address of pointer)

Обращаясь ко второй части вашего; когда использовать который:

  • Если эта структура находится в функции и вам нужно использовать ее после выхода из функции, вам нужно malloc. Вы можете использовать значение структуры, возвращая указатель, например: return q;.
  • Если эта структура временная и после нее вам не нужно ее значение, вам не нужно malloc память.

Использование с примером:

struct myStruct {
    int a;
    char *b;
};

struct myStruct *foo() {
    struct myStruct p;
    p.a = 5;
    return &p; // after this point, it's out of scope; possible warning
}

struct myStruct *bar() {
    struct myStruct *q = malloc(sizeof(struct myStruct));
    q->a = 5;
    return q;
}

int main() {
    struct myStruct *pMain = foo();
    // memory is allocated in foo. p.a was assigned as '5'.
    // a memory address is returned.
    // but be careful!!!
    // memory is susceptible to be overwritten.
    // it is out of your control.

    struct myStruct *qMain = bar();
    // memory is allocated in bar. q->a was assigned as '5'.
    // a memory address is returned.
    // memory is *not* susceptible to be overwritten
    // until you use 'free(qMain);'
}
0 голосов
/ 02 февраля 2020

Судя по вашим комментариям, вас интересует, когда использовать тот или другой. Обратите внимание, что все типы выделения резервируют память компьютера, достаточную для соответствия значению переменной в нем. Размер зависит от типа переменной. Статически размещенные переменные выводятся компилятором в определенное место в памяти. Автоматически размещенные переменные прикрепляются к месту в стеке одним и тем же компилятором. Динамически распределяемые переменные не существуют до запуска программы и не имеют места в памяти, пока они не будут распределены с помощью 'mallo c' или других функций.

Все именованные переменные назначаются статически или автоматически. Переменные Dynami c распределяются программой, но для того, чтобы иметь возможность получить к ним доступ, по-прежнему нужна именованная переменная, которая является указателем. Указатель - это переменная, которая достаточно велика, чтобы хранить адрес другой переменной. Последний может быть распределен динамически, статически или автоматически.

Вопрос в том, что делать, если ваша программа не знает, сколько объектов ей нужно использовать во время выполнения. Например, что если вы прочитаете некоторые данные из файла и создадите динамическую структуру c, например, список или дерево в вашей программе. Вы не знаете точно, сколько членов такой структуры у вас будет. Это основное использование динамически размещаемых переменных. Вы можете создать столько их, сколько нужно, и поместить все в список. В простейшем случае вам нужна только одна именованная переменная, которая указывает на начало списка, чтобы знать обо всех объектах в списке.

Другое интересное использование - когда вы возвращаете сложную структуру из функции. При автоматическом размещении в стеке он перестает существовать после возврата из функции. Динамически распределяемые данные будут постоянными, пока они не будут явно освобождены. Таким образом, использование динамического распределения c поможет здесь.

Есть и другие варианты использования.

В вашем простом примере нет большой разницы между обоими случаями. Второй требует дополнительных компьютерных операций, вызов функции 'mallo c', чтобы выделить память для вашей структуры. В первом случае память для структуры выделяется в установленной программной области c, определенной во время запуска программы. Обратите внимание, что указатель во втором случае также размещен статически. Он просто сохраняет адрес области памяти для структуры.

Также, как правило, динамически распределяемые данные должны в конечном итоге освобождаться функцией free. Вы не можете освободить данные о состоянии c.

0 голосов
/ 02 февраля 2020

Когда вы используете функцию mallo c, у вас останется ссылка на созданную память, и вы также можете освободить ее после ее использования при выполнении программы, используя ключевое слово free (). free (p_tr);

Но если вы используете простую инициализацию, вы не можете освободить память, память будет освобождена после полного выполнения программы.

0 голосов
/ 02 февраля 2020
struct person p = {.name="apple"};

^ Это автоматическое c выделение для переменной / экземпляра типа person.

struct person* p_tr = malloc(sizeof(person));

^ Это динамическое c выделение для переменной / экземпляра типа person.

Stati c выделение памяти происходит во время компиляции. Динамическое распределение c памяти означает, что оно выделяет память во время выполнения, когда программа выполняет эту строку инструкции

...