LINUX.ORG.RU

[си] calloc vs malloc

 


0

0

господа, вот вопросец, одновременно простой и непонятный

вот код с malloc

float *array = malloc(number_of_elements * sizeof(float));
if(!array)
{
   /* handle memory allocation error */
}
 
/* doing some stuff */

/* freeing resources */
free(array);
array = NULL;

а вот с calloc

float *array = calloc(number_of_elements, sizeof(float));
if(!array)
{
   /* handle memory allocation error */
}
 
/* doing some stuff */

/* freeing resources */
free(array);
array = NULL;

так вот вопрос, на первый взгляд - никакой разницы, а в чём фишка? зачем 2 функци с немного разным синтаксисом делающих одно и то же? есть что-то что я забыл (типа атомарности операции)?

★★★★★
Ответ на: комментарий от shty

просмотрел в манах :)

спасибо всем!

Совсем обнаглел, народ, его еще читать The Fucking Manual не послали, а он уже...

wfrr ★★☆
()

Мало того, что он обнуляет память, так он ещё (по крайней мере хороший calloc) проверяет параметры на предмет integer overflow. Цитата:

When using malloc() be careful to avoid the following idiom:

if ((p = malloc(num * size)) == NULL)
        err(1, "malloc");

The multiplication may lead to an integer overflow. To avoid this, calloc() is recommended.

If malloc() must be used, be sure to test for overflow:

if (size && num > SIZE_MAX / size) {
        errno = ENOMEM;
        err(1, "overflow");
}
undet
()
Ответ на: комментарий от shty

и да, кому интересно, вот тут грамотные (вроде?) примеры integer overflow

shty ★★★★★
() автор топика
Ответ на: комментарий от bk_

> calloc еще заполняет инициализированную память нулями

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

limux
()
Ответ на: комментарий от Legioner

> Ядро память святым духом обнуляет?

до вызова память вообще не могла быть обнулена, да?

где написано, что оно _гарантированно_ вернёт обнулённую память? цитату! быстро, решительно!

там ясно написано «может получить», а не «получит».

limux
()
Ответ на: комментарий от limux

> нечаянно, пьяным ПТУшником Васей же.

тогда ладно

lester ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

> ядро гарантирует.

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

limux
()
Ответ на: комментарий от limux

но сам calloc, при определённом стечении обстоятельств, может не тратить время на обнуление, т.к. память может быть уже обнулена.


интересно как он об этом узнаёт?!

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

интересно как он об этом узнаёт?!

возьми да почитай.

Роберт Лав. «Linux. Системное программирование». СПб.: Питер, 2008.

стр. 297

Позволять calloc() обнулять память, однако, быстрее, потому что ядро в этом случае предоставляет память, которая уже содержит нули."

стр. 321

Не используйте memset(), если можете использовать calloc()! Избегайте выделения памяти при помощи malloc() и немедленного последующего обнуления через memset(). Хотя результат может быть совершенно аналогичным, применять вместо двух функций один только вызов calloc(), который возвращает обнулённую память, намного лучше. Это не просто уменьшение на единицу количества вызовов - calloc() может получать от ядра уже обнулённую память, и в этом случае вы избегаете необходимости вручную устанавливать в ноль каждый байт, что повышает производительность.

дальше сам ищи, умник.

limux
()
Ответ на: комментарий от limux

на тебе в помощь реализацию calloc() из glibc 2.9

в комментариях всё написано.

Void_t*
public_cALLOc(size_t n, size_t elem_size)
{
  mstate av;
  mchunkptr oldtop, p;
  INTERNAL_SIZE_T bytes, sz, csz, oldtopsize;
  Void_t* mem;
  unsigned long clearsize;
  unsigned long nclears;
  INTERNAL_SIZE_T* d;
  __malloc_ptr_t (*hook) __MALLOC_PMT ((size_t, __const __malloc_ptr_t)) =
    __malloc_hook;

  /* 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) {
      MALLOC_FAILURE_ACTION;
      return 0;
    }
  }

  if (hook != NULL) {
    sz = bytes;
    mem = (*hook)(sz, RETURN_ADDRESS (0));
    if(mem == 0)
      return 0;
#ifdef HAVE_MEMCPY
    return memset(mem, 0, sz);
#else
    while(sz > 0) ((char*)mem)[--sz] = 0; /* rather inefficient */
    return mem;
#endif
  }

  sz = bytes;

  arena_get(av, sz);
  if(!av)
    return 0;

  /* Check if we hand out the top chunk, in which case there may be no
     need to clear. */
#if MORECORE_CLEARS
  oldtop = top(av);
  oldtopsize = chunksize(top(av));
#if MORECORE_CLEARS < 2
  /* Only newly allocated memory is guaranteed to be cleared.  */
  if (av == &main_arena &&
      oldtopsize < mp_.sbrk_base + av->max_system_mem - (char *)oldtop)
    oldtopsize = (mp_.sbrk_base + av->max_system_mem - (char *)oldtop);
#endif
  if (av != &main_arena)
    {
      heap_info *heap = heap_for_ptr (oldtop);
      if (oldtopsize < (char *) heap + heap->mprotect_size - (char *) oldtop)
	oldtopsize = (char *) heap + heap->mprotect_size - (char *) oldtop;
    }
#endif
  mem = _int_malloc(av, sz);

  /* Only clearing follows, so we can unlock early. */
  (void)mutex_unlock(&av->mutex);

  assert(!mem || chunk_is_mmapped(mem2chunk(mem)) ||
	 av == arena_for_chunk(mem2chunk(mem)));

  if (mem == 0) {
    /* Maybe the failure is due to running out of mmapped areas. */
    if(av != &main_arena) {
      (void)mutex_lock(&main_arena.mutex);
      mem = _int_malloc(&main_arena, sz);
      (void)mutex_unlock(&main_arena.mutex);
    } else {
#if USE_ARENAS
      /* ... or sbrk() has failed and there is still a chance to mmap() */
      (void)mutex_lock(&main_arena.mutex);
      av = arena_get2(av->next ? av : 0, sz);
      (void)mutex_unlock(&main_arena.mutex);
      if(av) {
        mem = _int_malloc(av, sz);
        (void)mutex_unlock(&av->mutex);
      }
#endif
    }
    if (mem == 0) return 0;
  }
  p = mem2chunk(mem);

  /* Two optional cases in which clearing not necessary */
#if HAVE_MMAP
  if (chunk_is_mmapped (p))
    {
      if (__builtin_expect (perturb_byte, 0))
	MALLOC_ZERO (mem, sz);
      return mem;
    }
#endif

  csz = chunksize(p);

#if MORECORE_CLEARS
  if (perturb_byte == 0 && (p == oldtop && csz > oldtopsize)) {
    /* clear only the bytes from non-freshly-sbrked memory */
    csz = oldtopsize;
  }
#endif

  /* Unroll clear of <= 36 bytes (72 if 8byte sizes).  We know that
     contents have an odd number of INTERNAL_SIZE_T-sized words;
     minimally 3.  */
  d = (INTERNAL_SIZE_T*)mem;
  clearsize = csz - SIZE_SZ;
  nclears = clearsize / sizeof(INTERNAL_SIZE_T);
  assert(nclears >= 3);

  if (nclears > 9)
    MALLOC_ZERO(d, clearsize);

  else {
    *(d+0) = 0;
    *(d+1) = 0;
    *(d+2) = 0;
    if (nclears > 4) {
      *(d+3) = 0;
      *(d+4) = 0;
      if (nclears > 6) {
	*(d+5) = 0;
	*(d+6) = 0;
	if (nclears > 8) {
	  *(d+7) = 0;
	  *(d+8) = 0;
	}
      }
    }
  }

  return mem;
}
limux
()
Ответ на: комментарий от limux

возьми да почитай.

Там нечего читать. Ты мне доказываешь что calloc быстрее чем malloc+memset, я это и сам знаю.

Ты написал что

сам calloc, при определённом стечении обстоятельств, может не тратить время на обнуление, т.к. память может быть уже обнулена.


меня интересует как он это узнаёт?! Сам механизм.

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

> меня интересует как он это узнаёт?! Сам механизм.

в предыдущем посте есть его реализация. интересно - посмотри. не пойму никак, ты меня подъеб^Wпроверить что ли хочешь? пятидневку что ли ввели? или уроки кончились уже?

limux
()
Ответ на: комментарий от limux

в предыдущем посте есть его реализация. интересно - посмотри.

не вижу можешь показать?! )

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

Ман:

Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2). When allocating blocks of memory larger than MMAP_THRESHOLD bytes, the glibc mal‐ loc() implementation allocates the memory as a private anonymous mapping using mmap(2). MMAP_THRESHOLD is 128 kB by default, but is adjustable using mallopt(3). Allocations performed using mmap(2) are unaffected by the RLIMIT_DATA resource limit (see getrlimit(2)).

Если количество меньше MMAP_THRESHOLD - то malloc+memset.
Если больше - то mmap() на fd=-1, и получаем у ядра с сразу с нулями.
Соответственно, о нулях узнаем из метода получения памяти.

vzzo ★★★
()
Ответ на: комментарий от Boy_from_Jungle

malloc это такая высокоуровневая хрень над mmap/sbrk. Борьба с фрагментацией и прочее живет в userspace.

Т.е. да, libc дергает вызовы более низкого уровня чем предоставляет тебе. Ты имеешь free/malloc; а libc обходиться mmap/munmap и sbrk.

catap ★★★★★
()
Ответ на: комментарий от vzzo

Соответственно, о нулях узнаем из метода получения памяти.

Да ладно, ничего мы так и не узнаем, и что-то я не знал что mmap возвращает обнулённую память.

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от catap

>А разве gnu libc не умеет этого прозрачно?

Он и делает это прозрачно - я описал примерный механизм работы calloc().

vzzo ★★★
()
Ответ на: комментарий от Boy_from_Jungle

>Да ладно, ничего мы так и не узнаем, и что-то я не знал что mmap возвращает обнулённую память.

Век живи - век учись :)

vzzo ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.