Является ли calloc (4, 6) так же, как calloc (6, 4)?

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

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

Ответ 1

То же самое. Причина в том, что большую часть времени вы хотите использовать оператор sizeof в качестве одного из аргументов. Если вам передают два параметра, вызовите malloc(), у которого есть один аргумент.

Ответ 2

Люди в основном используют процедуры распределения для выделения пространства для заданного количества элементов, поэтому calloc() позволяет это указать красиво. Так, например, если вам нужно пространство для 100 целых чисел или 20 вашей собственной структуры:

int *pInt = calloc (100, sizeof(int));
tMyStruct *pMyStruct = calloc (20, sizeof(tMyStruct));

Этот код выглядит немного "лучше", чем эквивалентные вызовы malloc():

int *pInt = malloc (100 * sizeof(int));
tMyStruct *pMyStruct = malloc (20 * sizeof(tMyStruct));

хотя, для опытных C-кодеров, нет никакого реального различия (кроме нулевой инициализации, конечно).

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

Ответ 3

calloc (4, 6) и calloc (6, 4) НЕ:

В типичной 32-битной/64-битной системе первый будет выделять 32 байта и второй 24 байта.


void *calloc(size_t nelem, size_t elsize);

Ключевым моментом является то, что calloc должен возвращать память, как если бы она была правильно выровнена как массив. Он предназначен для выделения массива и используется следующим образом:

A *p = (A*)calloc(count, sizeof(A));
for (int i = 0; i < count; ++i)
{
    f(&(p[i]));
    // f(p + i) is also valid
}

или

A *p = (A*)calloc(count, sizeof(A));
for (A *q = p; q != p + count; ++q)
{
    f(q);
}

calloc должен выделять массив с учетом отступов и других рабочих требований целевой системы. Таким образом, на большинстве 32-битных машин, где 6-байтовая структура должна быть дополнена до 8 байтов, она выделяет 4 лота 8 байтов.

calloc, где первый аргумент sizeof(), скорее всего, является ошибкой и должен быть исследован.

calloc, где второй аргумент не sizeof (atype) равен undefined. Он пахнет скрытыми предположениями и опасен для порта.

Разъяснение: В типичной 32-битной/64-битной системе структура, вероятно, будет дополнена и выровнена до кратного 32 бит. Таким образом, на этих системах sizeof не вернет 6 байтов. На самом деле нет никакой гарантии, что компилятор не будет заполнять и выровнять несколько кратных 16 байтов, если это требуется компилятору/платформе.

Мой ответ основан на том, что вы не должны делать предположений о размере структуры. Они могут меняться с помощью параметров компилятора или целевой платформы. Просто убедитесь, что ваш второй аргумент является выражением sizeof и не делает предположений.


Из стандарт:

Функция calloc() должна выделять неиспользуемое пространство для массива элементов nelem, размер каждого из которых в байтах является elsize. Пробел должен быть инициализирован ко всем битам 0.

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

Ответ 4

В отличные ответы, я хочу добавить еще одну разницу между использованием calloc(nelem, elsize) versus malloc(nelem * elsize): реализация качества calloc гарантирует, что если ваши nelem и elsize были достаточно большими, чтобы вызывают переполнение целого числа при умножении вместе, он будет терпеть неудачу, а не вызывать недораспределенное распределение, поскольку наивный вызов malloc будет.

Только для этой функции мне было бы предпочтительнее calloc - malloc. Фоновое чтение.

Ответ 5

Несмотря на принятый ответ (который, как я считаю, верен), кажется, есть путаницы в том, сколько байтов выделено из-за выравнивания. Итак, вот небольшой тест на моем 32-разрядном Linux с gcc-4.3:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  char* p1 = calloc(6, 4);
  char* p2 = calloc(4, 6);
  char* p3 = calloc(1,1);
  printf("%p, %p, %p\n", p1, p2, p3);
  return 0;
}

Результат:

0x826b008, 0x826b028, 0x826b048

который показывает, что оба calloc(6,4) и calloc(4,6) выделяют тот же объем памяти, который округлен до 32 байтов в моей системе. Изменение номеров до calloc(3,4) и calloc(4,3) даст следующий результат:

0x95d2008, 0x95d2018, 0x95d2028

который показывает, что 16 байтов зарезервированы, когда 12 запрошены и выделены программе. В любом случае вызовы calloc(a,b) и calloc(b,a) оказывают одинаковое влияние на использование памяти.


Добавил Джонатан Леффлер, потому что 300 персонажей никогда не будет достаточно.

Рассмотрим эту программу, которая утечки памяти, как настоящее сито, но демонстрирует точку:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int i, j, k;

    for (i = 1; i < 17; i++)
        for (j = 1; j < 9; j++)
            for (k = 0; k < 4; k++)
                printf("(%2d,%d)%d: %p\n", i, j, k, calloc(i, j));
    return(0);
}

В Windows под Cygwin это начинается с выделения блоков, разделенных 16 байтами (фактически, второй блок равен 24 байтам после первого, но после этого они разделены 16 байтами). При распределении (2,7) адреса блоков начинают увеличиваться на 24 байта; Аналогично, (3,4) выделяет блоки 16 байт, но (3,5) выделяет блоки 24 байта друг от друга. И, для записи, обе (4,6) и (6,4) обратные указатели 32 байта.

Это просто демонстрирует, что есть некоторые накладные расходы, связанные с вызовом распределения. Если вы посмотрите на архетипическую реализацию malloc() и др. В K & R, вы увидите, что размер блока хранится перед памятью, которую вы имеете право использовать. Различные реализации делают это по-другому; те, кто беспокоится о потере памяти, избегают хранения контрольных данных рядом с тем, где пользователь может нанести ущерб.

Когда вы вызываете calloc (4,6), у вас есть только надежный доступ к 24 байтам данных. Даже если ваша реализация дает вам возвращаемые значения, разделенные 32 байтами, вы можете безопасно использовать не более 24 байтов, которые вы запросили. И отладочные версии malloc() будут наблюдать, если вы выписываете из заданных вами границ.

Ответ 6

Существует небольшое различие: Calloc может решить обнулить память только по мере необходимости, а затем преимущество в знании размера элементов.

Я не могу назвать никаких реализаций, но это было задумано.

В качестве примера:

Один звонит 4 ГБ памяти, но система имеет всего 2 ГБ: не было бы смысла записывать в виртуальную память 2 ГБ нуля, поэтому система могла бы установить в этой памяти грязный флаг, чтобы обнулить его загружается в память.

Ответ 7

Есть еще один способ изучить этот вопрос.

Библиотека GNU C определяет calloc следующим образом:

void * __libc_calloc (size_t n, size_t elem_size)
{
  // ... (declarations)

  /* size_t is unsigned so the behavior on overflow is defined.  */
  bytes = n * elem_size;
#define HALF_INTERNAL_SIZE_T \
  (((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T) / 2))
  if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0))
    {
      if (elem_size != 0 && bytes / elem_size != n)
        {
          __set_errno (ENOMEM);
          return 0;
        }
    }

  void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    {
      sz = bytes;
      mem = (*hook)(sz, RETURN_ADDRESS (0));
      if (mem == 0)
        return 0;

      return memset (mem, 0, sz);
    }

  sz = bytes;

  // ...more stuff, but no mention of n & elem_size anymore
}

Итак, по крайней мере, в glibc эти два вызова имеют одинаковый эффект.