Вопрос

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

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

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

char name[256];

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

Это было полезно?

Решение

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

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

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

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

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

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

Конец добавления (Pax).

Другие советы

  

и что оба способа потребуют использования 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 не очень переносим среди компиляторов.

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

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

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

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

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

Полезно вспомнить весь " объявленный " vs " определено " вещь здесь Вот пример

/* 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 ;
}

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

Разбейте ваше утверждение

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.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top