Почему вы указываете размер при использовании malloc в C?

Возьмите следующий код:

int *p = malloc(2 * sizeof *p);

p[0] = 10;  //Using the two spaces I
p[1] = 20;  //allocated with malloc before.

p[2] = 30;  //Using another space that I didn't allocate for. 

printf("%d", *(p+1)); //Correctly prints 20
printf("%d", *(p+2)); //Also, correctly prints 30
                      //although I didn't allocate space for it

С линией malloc(2 * sizeof *p) я выделяю пространство для двух целых чисел, правильно? Но если я добавлю int в третью позицию, я все равно правильно распределю и получаю.

Итак, мой вопрос: , почему вы указываете размер при использовании malloc?

Ответ 1

Простая логика: если вы не парковаться на законном парковочном месте, ничего не может произойти, но иногда ваш автомобиль может быть отбуксирован, и вы можете застрять с огромным штрафом. И иногда, когда вы пытаетесь найти свой путь к фунту, где буксируется ваш автомобиль, вы можете столкнуться с грузовиком.

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

Для таких вопросов, раздел "Распределение памяти" в разделе "Часто задаваемые вопросы по С" ) является полезной ссылкой на консультации. См. 7.3b.

В связанной (юмористической) заметке см. также список bloopers ART.

Ответ 2

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

Отказ от ответственности: мое последнее реальное программирование на C было выполнено около 15 лет назад.

Ответ 3

Позвольте мне привести вам аналогию с тем, почему это "работает".

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

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

Когда закончите, вы делаете шаг назад и смотрите на свой рисунок, и он выглядит хорошо, как вы это делали, и точно так, как вы его нарисовали.

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

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

Кроме того, у этого человека теперь есть части вашего рисунка на его бумаге, возможно, возиться с тем, что он хотел иметь на бумаге.

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

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

Ответ 4

У вас (un) повезло. Доступ к p [3] равен undefined, так как вы не выделили эту память для себя. Чтение/списание конца массива - один из способов, с помощью которых C-программы могут сбой таинственным образом.

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

Хуже того, вы можете перезаписать некоторые другие данные и, возможно, не заметите. Представьте, что это случайно перезаписывает сумму денег, которую вы кому-то должны; -)

Ответ 5

Фактически, malloc не выделяет достаточно места для вашего третьего целого, но вы получили "повезло", и ваша программа не сработала. Вы можете только быть уверены, что malloc выделил именно то, что вы просили, не более того. Другими словами, ваша программа написала фрагмент памяти, который ему не был выделен.

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

Ответ 6

Это все возвращается к C, позволяя вам стрелять себе в ногу. Просто потому, что вы можете это сделать, это не значит, что вы должны. Значение в p + 3 определенно не гарантируется тем, что вы там помещаете, если вы специально не выделили его с помощью malloc.

Ответ 7

Попробуйте следующее:

int main ( int argc, char *argv[] ) {
  int *p = malloc(2 * sizeof *p);
  int *q = malloc(sizeof *q);
  *q = 100;

  p[0] = 10;    p[1] = 20;    p[2] = 30;    p[3] = 40;
  p[4] = 50;    p[5] = 60;    p[6] = 70;


  printf("%d\n", *q);

  return 0;
}

На моей машине он печатает:

50

Это связано с тем, что вы перезаписали память, выделенную для p, и топали на q.

Обратите внимание, что malloc не может помещать p и q в непрерывную память из-за ограничений выравнивания.

Ответ 8

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

Ответ 9

В зависимости от платформы p [500], вероятно, "работает" тоже.

Ответ 10

Вы запрашиваете пробел для двух целых чисел. p [3] предполагает, что у вас есть место для 4 целых чисел!

===================

Вам нужно указать malloc, сколько вам нужно, потому что он не может догадаться, сколько памяти вам нужно.

malloc может делать все, что захочет, если он возвращает не менее объем запрашиваемой памяти.

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

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

Ответ 11

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

В этот момент вы запросили то, что стандартные вызовы "Undefined Behavior", а компилятор и библиотека могут вообще что-либо делать в ответ. Разрешено даже "работать правильно".

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

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

Изменить: OP зафиксировал проблему с помощью *(p+2), отклоненную от p[1], поэтому я отредактировал свой ответ, чтобы удалить эту точку.

Ответ 12

Когда вы используете * (p + 3), вы обращаетесь за пределы даже с использованием 2 * sizeof (* p), поэтому вы получаете доступ к недопустимому блоку памяти, идеально подходящему для seg-сбоев.

В противном случае вы указываете размер b/c, функция не знает, сколько блоков из памяти кучи выделяется вашей программе для этого указателя.

Ответ 13

Потому что malloc() выделяет в BYTES. Итак, если вы хотите выделить (например) 2 целых числа, вы должны указать размер в байтах из 2 целых чисел. Размер целого может быть найден с помощью sizeof (int), и поэтому размер в байтах из 2 целых чисел равен 2 * sizeof (int). Поместите это все вместе, и вы получите:

int * p = malloc(2 * sizeof(int));

Примечание: учитывая, что вышеперечисленное выделяет пространство для двух целых чисел, вы очень непослушны в назначении третьего. Тебе повезло, что это не крушение.:)

Ответ 14

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

Ответ 15

Как все говорили, вы пишете в память, которая на самом деле не распределена, а это значит, что что-то может произойти, чтобы перезаписать ваши данные. Чтобы продемонстрировать проблему, вы можете попробовать что-то вроде этого:

int *p = malloc(2 * sizeof(int));
p[0] = 10; p[1] = 20; p[2] = 30;
int *q = malloc(2 * sizeof(int));
q[0] = 0; // This may or may not get written to p[2], overwriting your 30.

printf("%d", p[0]); // Correctly prints 10
printf("%d", p[1]); // Correctly prints 20
printf("%d", p[2]); // May print 30, or 0, or possibly something else entirely.

Невозможно гарантировать, что ваша программа будет выделять пространство для q при p [2]. На самом деле он может выбрать совершенно другое место. Но для простой такой программы это кажется вероятным, и если он выделяет q в месте, где p [2] будет, он будет явно демонстрировать ошибку вне диапазона.

Ответ 16

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

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

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

Ответ 17

Do:

int *p = malloc(2 * sizeof(*p)); // wrong (if type is something greater than a machine word)

[type] *p = malloc(2 * sizeof([type])); // right.