2013-02-22 4 views
8

Я запутался, при распределении памяти с malloc вы должны указать размер, однако иногда бывает, что вы не знаете, какой размер вам нужен в этот момент, чтобы вы либо выделили огромный объем памяти (который не кажется слишком мудрым, поскольку вы, вероятно, не собираетесь его использовать), или вы используете realloc, когда размер начального буфера становится слишком маленьким. Являются ли эти два варианта действительными? Второй звучит хорошо, однако, документы говорят, что realloc...may move the memory block to a new location, который звучит как очень плохая идея/трудно справиться с ситуацией (например, если у вас есть несколько указателей, указывающих на один и тот же адрес в тот момент, когда вы звоните realloc, все они становятся недействительными) m новичок с C, может ли кто-нибудь объяснить мне, как справиться с ситуацией, в которой у вас есть буфер, который может или не может вырасти, чтобы занять много памяти.Выбор размера для буфера

+4

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

+2

Использование макроса 'BUFSIZ' в' stdio.h' и дублирование его всякий раз, когда вы исчерпываете пространство, является общим подходом. –

+0

Кроме того, некоторые зависящие от платформы уродливые хаки включают использование 'malloc_size()' et al. – 2013-02-22 21:20:24

ответ

5

это иногда случается, что вы не знаете размера вам в тот момент, так что вы либо Alloc огромного количества памяти (которая не звучит слишком мудро, потому что вы, вероятно, не собираетесь его использовать все) или вы используете realloc, когда размер начального буфера становится слишком маленьким. Являются ли эти два варианта действительными?

В принципе да. На практике с ядрами ОС и конфигурациями системы по умолчанию не имеет значения, сколько вы выделите malloc. Понимаете, malloc выделяет адресное пространство, а не память. Вы можете выделить столько адресного пространства, сколько хотите, оно фактически не будет потреблять память; конечно, ОС будет использовать несколько проверок здравомыслия о стоимости, например, в системе с доступной только 2GiB-памятью (RAM + swap) вы не можете выделить 3GiB. Обычная конфигурация заключается в том, что наибольшая часть адресного пространства, выделяемого в одном блоке, составляет 50% доступной системной памяти.

Только когда вы на самом деле что-то им пишете, ОС зарезервирует для нее память. Поэтому не используйте calloc, потому что он инициализирует память, т. Е. Что-то записывает в нее.

Итак, если вы не знаете, сколько именно вам понадобится, просто malloc большой кусок адресного пространства, для которого вы знаете, по характеристикам обрабатываемых данных, что он легко будет держать все, что вы «ожидаю. После того, как вы приобрели его в памяти, вы можете использовать realloc, чтобы уменьшить выделение. Для всех реализаций, которые имеют значение realloc никогда не будет перемещать данные при сокращении выделения.

Одна вещь, о которой нужно помнить, это перезарядка памяти: скажем, что у вас есть 5 процессов, работающих в системе с 4GiB RAM, каждое распределение 1GiB, но не сразу записывающее на него. ОС предоставит им это адресное пространство, то есть оно переназначает память (подобно тому, как авиакомпании перегружают места для полетов). Через некоторое время процессы начинают писать. В какой-то момент у системы заканчивается память, и ОС должна что-то сделать: она начнет убивать процессы, пока не появится место для «дышать».

Вы можете отключить переполнение памяти; настоятельно рекомендуется в системах с высокой надежностью.

+0

Очень интересный ответ, однако, если вы записываете данные последовательно, так как только вы пишете первый срез, весь буфер будет выделен. Таким образом, вы не получаете слишком много. Только если вы сразу записываете свои данные. В любом случае, точка, принятая относительно 'calloc' /' malloc', спасибо. – Meda

+2

@Meda, а не весь буфер. Если я malloc 1M памяти и коснуться первого байта, система будет фиксировать страницу памяти (традиционно 4k, но может быть больше в современных системах), а не весь диапазон. –

+0

Ну, это делает его лучшим способом сделать это. Я думаю (malloc'ing огромное значение и не беспокойтесь об этом, так как любое современное ядро ​​справится с этим красиво, используя технику разбивки на страницы, которую вы описываете). Я чувствую, что мне нужно изменить мой принятый ответ, и будет делать это до тех пор, пока не возникнут дополнительные доказательства. – Meda

3

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

struct malloc_wrapper 
{ 
    void *p; 
} wrapper; 

wrapper.p = malloc(INITIAL_SIZE); 

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

void *tmp = realloc(somepointertowrapper->p, NEW_SIZE); 

/* check tmp to ensure it's not NULL. That indicates a failure 
* to realloc and the original pointer passed into realloc 
* remains valid. 
*/ 

somepointertowrapper->p = tmp; 
+0

Спасибо, это звучит хорошо. – Meda

+0

Всегда проверяйте результат с 'realloc' перед тем, как назначить его указателю для обнаружения сбоев распределения. Имейте в виду, что 'realloc' является интерфейсом ** HORRIBLE **, потому что он пытается быть всем для всех людей. Я не шучу: вы можете избежать использования 'malloc' и' free' и делать * все * с помощью 'realloc'. –

Смежные вопросы