Как вы узнаете, сколько места нужно выделить с помощью malloc ()? - PullRequest
10 голосов
/ 08 августа 2009

Я полный новичок в C, я из C #. Я изучал управление памятью и функцию malloc(). Я также сталкивался с этим кодом:

char *a_persons_name = malloc(sizeof(char) + 2);

Чего я не понимаю, так это как много места выделяется для a_persons_name. Это распределение 2 символов (например, AB) или что-то еще?

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

Ответы [ 6 ]

17 голосов
/ 08 августа 2009

Этот фрагмент выделяет достаточно места для двухсимвольного имени.

Как правило, строковый буфер будет заполняться откуда-то, т. Е. Ввод / вывод. Если размер строки не известен заранее (например, чтение из файла или клавиатуры), обычно используется один из трех подходов:

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

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

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

5 голосов
/ 08 августа 2009

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

На то, что вы выделяете, достаточно места для трех персонажей. Но имейте в виду, что вам нужен один для нулевого терминатора для строк C.

То, что вы склонны находить, это такие вещи:

#define NAME_SZ 30
: : :
char *name = malloc (NAME_SZ+1);

чтобы получить достаточно памяти для имени и символа-терминатора (учитывая, что строка "xyzzy" хранится в памяти как:

+---+---+---+---+---+----+
| x | y | z | z | y | \0 |
+---+---+---+---+---+----+

Иногда с массивами, не основанными на символах, вы увидите:

int *intArray = malloc (sizeof (int) * 22);

, который выделит достаточно места для 22 целых чисел.

3 голосов
/ 08 августа 2009

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

оператор sizeof дает размер своего аргумента в байтах.

char *someString = malloc(sizeof(char) * 50)

это выделит достаточно места для строки из 49 символов (строка в стиле C должна заканчиваться символом NULL ('\0')), не включая символ NULL, и указывает someString в этой памяти.

Похоже, что код в вашем вопросе должен быть malloc(sizeof(char) * 2);, так как sizeof(char) + 2 не имеет смысла.

обратите внимание, что sizeof(char) гарантированно всегда равен 1 (байт) - но представление в памяти других типов (таких как long) может варьироваться в зависимости от компилятора.

Способ, которым вам (не) везет с динамически выделенной памятью, заключается в том, что вы пытаетесь читать / писать вне выделенной памяти.

Например,

char *someString = malloc(10);
strcpy(someString, "Hello there, world!");
printf("%s\n", someString);

Первая строка выделяет достаточно места для 9 символов и символа NULL.
Вторая строка пытается скопировать 20 символов (19 + NULL) в это пространство памяти. Это приводит к переполнению буфера и может привести к чему-то невероятно остроумному, например, к перезаписи смежной памяти или к ошибке segfault.

Третья строка может сработать, например, если рядом с someString выделена память, и "Привет, мир!" столкнулся с этим пространством памяти, он может напечатать вашу строку плюс все, что было в следующем пространстве памяти. Если бы этот второй пробел был завершен до NULL, он бы остановился - если только он не был, в этом случае он отошел бы и в конечном итоге перестал работать.

Этот пример - довольно простая операция, но так легко ошибиться. С хитростью - будь осторожен.

1 голос
/ 08 августа 2009

Первая точка - это хорошая привычка никогда не ставить абсолютные числа в аргументе для malloc, всегда использовать sizeof и кратное число. Как сказано выше, память, выделенная для некоторых типов, зависит от компилятора и платформы. Чтобы гарантировать достаточно места для массива типа 'blob', лучше использовать что-то вроде этого:

blob *p_data = malloc(sizeof(blob) * length_of_array);

Таким образом, каким бы ни был тип, однако он выглядит в памяти, вы получите абсолютно правильное количество.

Во-вторых, segfaults и т. Д. C, как язык низкого уровня, не имеет проверки границ. Это означает, что вам нечего проверять, смотрите ли вы индекс, которого нет в массиве. На самом деле это не мешает вам получить доступ к памяти в любом месте, даже если она не принадлежит вашей программе (хотя ваша операционная система может, вот что такое segfault). Вот почему всякий раз, когда вы передаете массив в C, вы также должны передавать его длину, чтобы функция, принимающая массив, знала, насколько он большой. Не забывайте, что массив - это просто указатель на первый элемент. Это очень бесполезно при передаче строк - каждый строковый аргумент становится двумя аргументами, поэтому используется чит. Любая стандартная строка C заканчивается на NULL. Последний символ в строке должен иметь значение ASCII 0. Любые строковые функции работают вдоль массива, пока не увидят это и не остановятся. Таким образом, они не переполняют массив, но если его по какой-то причине нет, они будут. Это понятно

strlen("Hello")

равно 5, но для его хранения вам понадобится еще один символ. E.g.:

const char str1 = "Hello";
char *str2 = malloc(sizeof(char) * (strlen(str1) + 1));
strcpy(str2, str1);

И да, sizeof (char) не нужен, потому что он определен как 1, но я нахожу это более понятным, и это определенно хорошая привычка.

1 голос
/ 08 августа 2009

Это выделит три байта; 1 для sizeof (char), плюс два. Просто видя эту строку вне контекста, я не могу понять, почему она будет выделена таким образом или она верна (мне это кажется подозрительной).

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

1 голос
/ 08 августа 2009

Ваш вызов malloc выделит 3 байта памяти. sizeof(char) равен 1 байту, а 2 байта указаны явно. Это дает вам достаточно места для строки размера 2 (вместе с символом завершения)

...