char x [256] против char * = malloc (256 * sizeof (char)); - PullRequest
13 голосов
/ 07 января 2009

Кто-то недавно указал мне на мой код, который я использую

char* name = malloc(256*sizeof(char));
// more code
free(name);

У меня сложилось впечатление, что этот способ настройки массива идентичен использованию

char name[256];

и что оба способа потребуют использования free (). Я не прав, и если да, то может ли кто-нибудь объяснить в низком смысле, в чем разница?

Ответы [ 6 ]

27 голосов
/ 07 января 2009

В первом коде память динамически распределяется по куче. Эта память должна быть освобождена с помощью free (). Его время жизни произвольно: оно может пересекать границы функций и т. Д.

Во втором коде 256 байтов распределяются в стеке и автоматически восстанавливаются, когда функция возвращается (или при завершении программы, если она находится вне всех функций). Таким образом, вы не должны (и не можете) вызвать free () для этого. Он не может течь, но он также не будет жить после окончания функции.

Выберите один из двух вариантов в зависимости от требований к памяти.

Приложение (Pax):

Если я могу добавить к этому, Нед, большинство реализаций обычно предоставляют больше кучи, чем стека (по крайней мере, по умолчанию). Обычно это не имеет значения для 256 байтов, если вы уже не исчерпали свой стек или не выполняете сильно рекурсивную работу.

Кроме того, sizeof (char) всегда равен 1 в соответствии со стандартом, поэтому вам не нужно это лишнее умножение. Несмотря на то, что компилятор, вероятно, оптимизирует его, он делает код ужасным IMNSHO.

Конечное дополнение (Pax).

7 голосов
/ 07 января 2009

и что оба способа потребуют использования free ().

Нет, только первый нуждается в бесплатном использовании. Второй размещается в стеке. Это делает его невероятно быстрым для распределения. Смотрите здесь:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

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

Использование malloc отличается. Он закажет произвольный большой кусок памяти (из места, которое называется freestore ). Среде выполнения придется искать достаточно большой блок памяти. Размер можно определить во время выполнения, поэтому компилятор обычно не может оптимизировать его во время компиляции. Поскольку указатель может выходить из области видимости или копироваться, отсутствует внутренняя связь между выделенной памятью и указателем, которому назначен адрес памяти, поэтому память все еще выделяется, даже если вы давно покинули функцию , Вы должны позвонить бесплатно, передав адрес, который вы получили от malloc вручную, если пришло время сделать это.

Некоторая «недавняя» форма C, называемая C99, позволяет вам давать массивам размер времени выполнения. Т.е. вам разрешено делать:

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

Но эту функцию лучше избегать, если у вас нет причин ее использовать. Одной из причин этого является то, что это не отказоустойчиво: если больше нет доступной памяти, может произойти все что угодно Другое дело, что C99 не очень переносим среди компиляторов.

5 голосов
/ 07 января 2009

Здесь существует третья возможность: массив может быть объявлен внешним по отношению к функции, но статически, например,

// file foo.c
char name[256];

int foo() {
    // do something here.
}

Я был довольно удивлен в ответах на другой вопрос о SO, что кто-то посчитал это неуместным в C; здесь это даже не упоминается, и я немного смущен и удивлен (например, «чему они учат детей в школе в эти дни?») по этому поводу.

Если вы используете это определение, память распределяется статически не в куче и не в стеке, а в пространстве данных на изображении. Таким образом, управление не должно осуществляться так же, как в случае с malloc / free, и вам не нужно беспокоиться о повторном использовании адреса, как при автоматическом определении.

Полезно вспомнить здесь всю "объявленную" или "определенную" вещь. Вот пример

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

Теперь в другом файле

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}
2 голосов
/ 07 января 2009

Разбейте ваше заявление

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

Перевод: имя теперь является указателем на символ, которому назначен адрес некоторой памяти в куче

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

Перевод: Имя - это постоянный указатель на символ, который указывает на некоторую память в стеке

В Си массивы и указатели - это более или менее одно и то же. Массивы являются постоянными указателями на память. Основное отличие состоит в том, что когда вы вызываете malloc, вы берете свою память из кучи, и любая память, взятая из кучи, должна быть освобождена из кучи. Когда вы объявляете массив с размером, ему назначается память из стека. Вы не можете освободить эту память, потому что она освобождает память из кучи. Память в стеке будет автоматически освобождена, когда вернется текущий программный модуль. Во втором примере free (p) также будет ошибкой. p - указатель массива имен в стеке. Таким образом, освобождая p, вы пытаетесь освободить память в стеке.

Это ничем не отличается от:

int n = 10;
int *p = &n;

освобождение p в этом случае будет ошибкой, поскольку p указывает на n, который является переменной в стеке. Следовательно, p хранит ячейку памяти в стеке и не может быть освобожден.

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

в этом случае свободный является правильным, потому что p указывает на область памяти в куче, которая была выделена malloc.

2 голосов
/ 07 января 2009

Это неверно - объявление массива не требует свободного. Кроме того, если это внутри функции, она распределяется в стеке (если память служит) и автоматически освобождается при возврате функции - не передавайте ссылку на нее обратно вызывающей стороне!

0 голосов
/ 11 февраля 2009

в зависимости от того, где вы работаете, место в стеке может быть ОГРОМНЫМ. Например, если вы пишете код BREW для телефонов Verizon / Alltel, вы, как правило, ограничены минимальными стеками, но имеете постоянно увеличивающийся доступ к куче.

Кроме того, поскольку char [] чаще всего используются для строк, неплохо было бы позволить методу построения строк выделять память, необходимую для рассматриваемой строки, а не надеяться, что навсегда и всегда 256 какой бы номер вы ни указали) будет достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...