David Kviloria

Understanding Memory Initialization in C

Memory is a vital aspect of programming, regardless of what you’re building. Understanding manual memory management is key for performance-critical systems, embedded development, and debugging low-level bugs. There are three key high level aspects you need to understand:

  1. Allocation
  2. Usage
  3. Deallocation

When you allocate memory on heap, you’re requesting a block of memory from the operating system. This memory isn’t automatically initialized, which means it contains whatever values were previously stored there.

// allocate 8 bytes of memory
char* buffer = (char*)malloc(8);

After allocation, you should initialize your memory to known values to avoid unpredictable behavior.

// initialize all bytes to zero
memset(buffer, 0, 8);

Or we could use calloc, which will allocate memory on heap like malloc but additionally it will initialize the values

// allocate and zero-initialize 8 bytes
char* buffer = (char*)calloc(8, sizeof(char));

Forgetting to initialize memory

char* data = (char*)malloc(4);

// 'data' contains uninitialized (garbage) values
printf("First byte: %d\n", data[0]);

Consider following more practical example

Imagine that we have simple sequence of frames that makes animation, and we want to display it in our game

typedef struct AnimationPlayerState {
  int speed;
  int index;
  int length;
} AnimationPlayerState;

#define ANIM_FRAME_COUNT 8
#define MAX_STR_LEN 32

const char frame_names[ANIM_FRAME_COUNT][MAX_STR_LEN] = {
  "player-jump-frame-0",
  "player-jump-frame-1",
  "player-jump-frame-2",
  "player-jump-frame-3",
  "player-jump-frame-4",
  "player-jump-frame-5",
  "player-jump-frame-6",
  "player-jump-frame-7",
};

Then for this example sake, let’s allocate AnimationPlayerState on heap

AnimationPlayerState* anim_state = (...*)malloc(sizeof(AnimationPlayerState));
if (anim_state == NULL) {
  // We were not given piece of memory to work with.
}

At this point, memory is allocated, but fields like index and length are left uninitialized

Problem arises when we try to use those members

void Update(AnimationPlayerState* anim_state)
{
  // attempting to access a frame using an uninitialized index
  // potential out-of-bounds access
  const char* frame = frame_names[anim_state->index++];

  // 'index' was not initialized
  // it could be any value, negative, very large, or random.
  // accessing frame_names out of bounds results in UB
  // and possibly crashing the program.
}

You can avoid this by either initializing the struct manually or using calloc as we mentioned above:

But this would still require additional adjustments for members for their specific roles

AnimationPlayerState* anim_state = (...*)calloc(1, sizeof(AnimationPlayerState));

This zeroes all fields, but you may still need to explicitly set meaningful defaults:

anim_state->speed = 1;
anim_state->length = ANIM_FRAME_COUNT;

Or alternatively, initialize it manually

Preferably if you have members that have different responsibilities, like here

AnimationPlayerState* anim_state = (...*)malloc(sizeof(AnimationPlayerState));
anim_state->speed = 1;
anim_state->index = 0;
anim_state->length = ANIM_FRAME_COUNT;

Interactive Memory Visualization

Below is an interactive visualization that demonstrates the different states of memory throughout its lifecycle. Click on the visualization to cycle through the states and see how memory changes:

Click to see different memory states including: uninitialized, initialized, filled, pointer access, memory leak, and freed memory.

#Memory Management #C Programming