Два аргумента для calloc

Почему calloc принимает два аргумента вместо одного типа malloc?

В частности, поскольку между выражениями (или есть?) нет разницы между следующими выражениями:

calloc (a, b);
calloc (b, a);
calloc (a * b, 1);
calloc (1, a * b);

Почему бы просто не принять общее количество байтов для распределения? В чем смысл этого интерфейса? И почему это не относится к malloc?

Ответы

Ответ 1

Я слышал два [взаимоисключающих] объяснения, почему у него есть два аргумента:

  • calloc берет на себя ответственность за проверку переполнения при умножении. Если общий размер запрошенного блока слишком велик (например, переполнение size_t), calloc возвращает пустой указатель, указывающий на сбой. С помощью malloc вы должны следить за переполнением себя, что многие люди просто забывают делать. (Хотя история стандартной библиотеки знает примеры реализаций calloc, которые игнорировали переполнение и, следовательно, работали некорректно).

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

Я всегда считал, что первое объяснение является правильным. Однако после прочтения некоторых сообщений здесь, на SO, у меня есть свои сомнения.

Ответ 2

Я считаю, что malloc гарантированно вернет область памяти, которая выровнена в соответствии с самым грубым требованием, которое будет совместимо с размером, указанным вторым аргументом. Например, если система требует выравнивания 2 и 4 байтовых целых чисел, а второй аргумент равен 10, возвращаемый указатель должен быть выровнен на двухбайтовой границе; если второй аргумент равен 12, указатель будет выровнен на четырехбайтовой границе. Я подозреваю, что на практике многие системы будут выровнять все возвращенные указатели с наибольшей возможной границей независимо от размера, но я не думаю, что это необходимо, кроме calloc.

Ответ 3

calloc(x,y) является эквивалентом malloc(x*y)

Но calloc делать дополнительные (значения уставок 0 с) memset(block, 0, x*y)

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

Например, если вы хотите выделить память для 12 целых чисел, и вы хотите сделать что-то с этими целыми числами, и вы должны были установить ее значения в 0, используйте calloc(12, sizeof(int))

Но если вы хотите выделить какой-то блок памяти (256 байтов), чтобы скопировать в будущем некоторую строку, тогда memset не подходит для вас, тогда лучше использовать malloc(sizeof(char) * 256) или, например, malloc(sizeof(wchar_t) * 256)


void *
calloc (size_t nmemb, size_t lsize)
{
  void *ptr;
  struct __meminfo *info;
  size_t size = lsize * nmemb;

  /* if size overflow occurs, then set errno to ENOMEM and return NULL */
  if (nmemb && lsize != (size / nmemb))
    {
      set_errno (ENOMEM);
      return NULL;
    }

  /* allocate memory */
  ptr = malloc (size);

  /* get pointer to info part of chunk */
  info = __mem2info (ptr);

  /* fill memory with zeros and set __MEM_CALLOC flag */
  memset (ptr, 0, info->size);
  info->flags |= __MEM_CALLOC;

  return ptr;                   /* happy end */
}

Ответ 4

Единственная заметная разница заключается в том, что calloc требуется для инициализации выделенного пространства для нулей, пока нет такой гарантии с malloc. В противном случае, я думаю, есть две разные функции только по историческим причинам.

Ответ 5

Все просто байты - это относительно новое (например, c/Unix) изобретение - на многих других архитектурах были фиксированные записи.