An in-depth look at memory use in C

RAM BoardThis is a standalone tutorial about us of memory in C. It covers everything from declaring variables in global RAM, local (stack) memory in functions, and declaring heap memory.

The same principles should apply to Windows, Linux or Mac OS programming.

A look at Memory (RAM)  Use in C programs

When you write a program in C, chances are it will need to allocate RAM for data as well as code. I’m not looking at the code side of things here, it’s all about the data.

Here’s a list of all the different places it can be used

  1. Immutable memory for strings.
  2. Data segment memory for variables declared outside of functions.
  3. Uninitialized variables. This is known as BSS (short for Block Starting Symbol) area.
  4. Local (Stack) memory inside functions.
  5. Memory allocated from the heap through malloc etc.
  6. CPU caches. These speed up retrieval of data nd CPU instructions.

The simple overview of memory usage by a program.

When the Operating System (OS) loads a program into RAM, it will allocate different areas of RAM.

  1. Program code. Ideally this should be read only- why would you want to overwrite code?
  2. Data Area. Where variables are stored.
  3. Uninitialized area BSS. This holds unitialized variables.
  4. Text area. Read-only (hopefully!) area where text strings are held. This may vary according to compiler and operating system so I can’t say definitely where they are held.
  5. A fixed size block of RAM for the stack. The size is the maximum size of the stack and can be specified.
  6. The rest of memory available as the heap. You don’t get this automatically, you have to request with calls to malloc, calloc etc.

From the perspective of our program RAM is just a big block of bytes. How the hardware manages it isn’t important.

Overwriting Text Strings

This is not a good idea but I was curious to see what happens if I try it.

Here’s a short program that attempts that. It declares a string name and attempts to write to it.

#include<stdio.h>
#include <string.h>

char* name="My name is David";
int main()
{    
    strcpy_s(name,10, "New name");
    printf("Name = %s\n", name);
    return 0;
}

In Visual C++ on Windows when I ran this it blew up with an exception doing the strcpy_s() function.  Of course C doesn’t do exceptions so it just stopped and output nothing.

Exception thrown at 0x7AEF000F (ucrtbased.dll) in calc.exe: 0xC0000005: Access violation writing location 0x00C47BE8.

So don’t try overwriting text literals!

Accessing Data areas

Here’s a short C program that tries to use many of these areas of RAM. It will not win any prizes for beauty,

#include<stdio.h>

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = { 23,34,12,17,204,99,16 };
int c;

int Increment() {
    static int value=-1;
    value++;
    return value;
}

int main()
{
    for (int i = 0; i < 7; i++) {
        int a = Increment();
        printf("a=%d array[a]=%d\n", a,array[a]);
    }
    c = 47;
    return 0;
}

Let’s look at the different variables.

The unimaginatively named array is stored in global RAM. It will be located in the Data segment as it has values. Also the c variable will be there as well. Also, the badly named value in the function Increment() is also there. It is declared static which means the value persists between calls to Increment(). A non-static variable declared in a function is normally stored in stack memory but static variables are stored in data.

Remember main() is also a function so variables like i and a are declared within it and are this stack variables.  You can see this by looking at a disassembly of the code. The [ebp-8] is the location of the i. The ebp is a CPU register that points to the stack. Any local variable will be accessed via ebp unless it is a static.

    for (int i = 0; i < TOTAL_ELEMENTS; i++) {
00561C98  mov         dword ptr [ebp-8],0 

Likewise for a which is stored at [ebp-14h]

    int a = Increment();
00561CB0 call _Increment (05613F7h)
00561CB5 mov dword ptr [ebp-14h],eax

However the value stored in c is stored in the data segment.

    c = 47;
00561CD6  mov         dword ptr [_c (056A174h)],2Fh  

When run, this is what the program outputs:

a=0 array[a]=23
a=1 array[a]=34
a=2 array[a]=12
a=3 array[a]=17
a=4 array[a]=204
a=5 array[a]=99
a=6 array[a]=16

Static Variables

In the function increment, the variable value is declared as static with an initial value of -1. Although declared in a function, because it’s declared static, the variable is stored in the data segment. To increment the value, it fetches it from RAM into the eax register, adds 1 to it then stores it back in 02BA01Ch.

    value++;
002B1AE8  mov         eax,dword ptr [value (02BA01Ch)]  
002B1AED  add         eax,1  
002B1AF0  mov         dword ptr [value (02BA01Ch)],eax

The clever bit with static variables is that you initialise them in the declaration and they can only be accessed from within Increment.

Heap Memory

If you wish to access RAM outside what has been declared (and is thus in Data or BSS areas) requires you to request a block from the OS. This is what you call malloc() for.  Just tell it how many bytes you require and it will return a pointer to the allocated block. You need to cast this to some type as malloc() returns a void * meaning it is just a raw pointer and no good for accessing data. The compiler needs to know what type of data a pointer refers to.

In this program, a typedef buffer is defined as a 1000 char array.  A ptr to a buffer is declared and assigned to malloc(sizeof(buffer)). You always need to pass into malloc() exactly how many bytes you wish to acquire.

To confirm that it allocated 1000 bytes the sizeof(*ptr) returns 1000 in the printf.  Once you have finished with the memory you must release it by calling free. A good rule of thumb is that “For every malloc there should be a matching free”.

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

typedef char buffer[1000];

buffer* ptr;

int main()
{    
    ptr = (buffer*)malloc(sizeof(buffer));
    printf("Sizeof(*ptr)= %u\n", sizeof(*ptr));
    //do something with memory
    free(ptr);
    return 0;
}
(Visited 236 times, 1 visits today)