Когда следует использовать malloc в C, а когда нет?

Я понимаю, как работает malloc(). Мой вопрос: я увижу такие вещи:

#define A_MEGABYTE (1024 * 1024)

char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);

Я пропустил проверку ошибок для краткости. Мой вопрос: не можете ли вы просто сделать это, инициализируя указатель на некоторое статическое хранилище в памяти? возможно:

char *some_memory = "Hello World";

В какой момент вам действительно нужно выделить память самостоятельно, а не объявлять/инициализировать значения, которые необходимо сохранить?

Ответ 1

char *some_memory = "Hello World";

создает указатель на строчную константу. Это означает, что строка "Hello World" будет находиться где-то в части только для чтения в памяти, и у вас есть указатель на нее. Вы можете использовать строку как доступную только для чтения. Вы не можете вносить в него изменения. Пример:

some_memory[0] = 'h';

Запрашивает проблемы.

С другой стороны,

some_memory = (char *)malloc(size_to_allocate);

выделяет массив char (переменная), а some_memory указывает на выделенную память. Теперь этот массив читается и записывается. Теперь вы можете:

some_memory[0] = 'h';

и содержимое массива изменится на "hello World"

Ответ 2

Для этого точного примера malloc мало полезен.

Основная причина, по которой требуется malloc, - это когда у вас есть данные, которые должны иметь срок службы, отличный от области кода. Ваш код вызывает malloc в одной подпрограмме, где-то хранит указатель и в конечном итоге вызывает вызов в другой процедуре.

Вторая причина заключается в том, что C не имеет возможности узнать, достаточно ли свободного места в стеке для распределения. Если ваш код должен быть на 100% надежным, безопаснее использовать malloc, потому что тогда ваш код может знать, что выделение не удалось и обрабатывать его.

Ответ 3

malloc - замечательный инструмент для распределения, перераспределения и освобождения памяти во время выполнения, по сравнению со статическими объявлениями, такими как ваш пример hello world, которые обрабатываются во время компиляции и, следовательно, не могут быть изменены по размеру.

Malloc всегда полезен, когда вы имеете дело с данными произвольного размера, такими как чтение содержимого файла или работа со сокетами, и вы не знаете о длине обрабатываемых данных.

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

Ответ 4

char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");

является незаконным, строковые литералы const.

Это позволит выделить 12-байтовый массив char в стеке или глобально (в зависимости от того, где он был объявлен).

char some_memory[] = "Hello World";

Если вы хотите оставить место для дальнейших манипуляций, вы можете указать, что размер массива должен быть больше. (Пожалуйста, не кладите 1MB в стек.)

#define LINE_LEN 80

char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);

Ответ 5

Одна из причин, когда необходимо выделить память, - это если вы хотите изменить ее во время выполнения. В этом случае можно использовать malloc или буфер в стеке. Простой пример назначения "Hello World" указателю определяет память, которая "обычно" не может быть изменена во время выполнения.

Ответ 6

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


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

int do_something() {
    uint8_t* someMemory = (uint8_t*)malloc(1024);

    // Do some stuff

    if ( /* some error occured */ ) return -1;

    // Do some other stuff

    free(someMemory);
    return result;
}

Вы видите, что не так с этим кодом? Существует условное выражение возврата между malloc и free. Сначала это может показаться хорошо, но подумайте об этом. Если есть ошибка, вы вернетесь, не освободив выделенную память. Это общий источник утечек памяти.

Конечно, это очень простой пример, и здесь очень легко увидеть ошибку, но представьте себе сотни строк кода, заваленных указателями, malloc s, free s и всеми видами обработки ошибок. Вещи могут стать действительно грязными очень быстро. Это одна из причин, по которой я предпочитаю современный С++ по сравнению с C в применимых случаях, но это целая тема.

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