Ich habe das mit folgendem Programm probiert:

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

#define ONECHUNK

int main(void)
{
#ifdef ONECHUNK
  (void)malloc(10000000);
#else
  int allocCount;

  for (allocCount=0; allocCount<1000000; allocCount++) {
    (void)malloc(10);
  }
#endif
  (void)fgetc(stdin);
  return 0;
}

– die void-Casts vor den Funktionsaufrufen sollen deutlich machen, dass ich weiß was ich tue, wenn ich den Rückgabewert ignoriere. Manche Compiler warnen sogar, wenn man den Rückgabewert einer non-void-Funktion einfach wegwirft, und mit einem Cast dieser Art kann man sie beruhigen. Strikt nötig ist er natürlich nicht, und es ist auch nicht falsch, wenn ihr die Rückgabewerte an “dummy”-Variablen zugewiesen habt.

Bei mir war die VSZ 13624, wenn ich ONECHUNK definiert hatte, und 19480, wenn ich es nicht definiert hatte (was am elegantesten geht, wenn man das define einfach durch ein undef ersetzt – undef löscht Präprozessorsymbole). Vor dem Allozieren hat das Programm eine VSZ von 2852 gehabt (was nicht heißt, dass es wirklich so viel Speicher belegt, aber das ist ein anderes Thema), ihr könnt also sehen, dass beim Allozieren von kleinen Blöcken etwa 50% mehr Speicher belegt wird.

Woher der Unterschied? Nun, malloc muss irgendwo Buch führen, wie groß die allozierten Speicherblöcke sind wo sie liegen. Eine ganz einfache Methode dazu könnte sein, vor jedem Block seine Größe und einen Pointer auf den nächsten Block zu speichern (einfach verkettete Liste) – auf einer 32-bit-Maschine braucht man dafür 8 Byte, und wenn man je 10 Byte alloziert, ist dieser Verwaltungsoverhead fast 50% der Nutzdaten. In Wirklichkeit verwendet jedenfalls das malloc der GNU-Bibliothek eine raffiniertere Methode, die auch nicht so anfällig gegen Überschreiben dieser Information durch Fehler im Programm sind, aber ein Overhead bleibt natürlich.

Moral: Wenns geht, nicht immer ganz winzige Speicherstücke allozieren.