|< C & C++ Compiler, Assembler, Linker & Loader | Main | C Storage Class & Memory 2 >| C++/OOP | Site Index | Download |


 

 

 

MODULE Z

THE C STORAGE CLASSES, SCOPE AND MEMORY ALLOCATION 1

 

 

 

 

My Training Period: zz  hours

 

Note: gcc compilation examples are given at the end of this Module.

 

The skills that supposed to be acquired:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Z.1  INTRODUCTION

  • The storage class determines the part of memory where storage is allocated for an object (particularly variables and functions) and how long the storage allocation continues to exist.

  • A scope specifies the part of the program which a variable name is visible, that is the accessibility of the variable by its name.  In C program, there are four storage classes: automatic, register, external, and static.

  • Keep in mind that in the hardware terms we have primary storage such as registers, cache, memory (Random Access Memory) and secondary storage such as magnetic and optical disk.

Z.1.1  AUTOMATIC VARIABLE - auto

  • They are declared at the start of a program’s block such as in the curly braces ( { } ).  Memory is allocated automatically upon entry to a block and freed automatically upon exit from the block.

  • The scope of automatic variables is local to the block in which they are declared, including any blocks nested within that block. For these reasons, they are also called local variables.

  • No block outside the defining block may have direct access to automatic variables (by variable name) but, they may be accessed indirectly by other blocks and/or functions using pointers.

  • Automatic variables may be specified upon declaration to be of storage class auto.  However, it is not required to use the keyword auto because by default, storage class within a block is auto.

  • Automatic variables declared with initializers are initialized every time the block in which they are declared is entered or accessed.

Z.1.2  REGISTER VARIABLE - register

  • Automatic variables are allocated storage in the main memory of the computer; however, for most computers, accessing data in memory is considerably slower than processing directly in the CPU.

  • Registers are memory located within the CPU itself where data can be stored and accessed quickly.  Normally, the compiler determines what data is to be stored in the registers of the CPU at what times.

  • However, the C language provides the storage class register so that the programmer can suggest to the compiler that particular automatic variables should be allocated to CPU registers, if possible and it is not an obligation for the CPU to do this.

  • Thus, register variables provide a certain control over efficiency of program execution.

  • Variables which are used repeatedly or whose access times are critical may be declared to be of storage class register.

  • Variables can be declared as a register as follows:

register  int  var;

 

Z.1.3  EXTERNAL VARIABLE - extern

extern int var;

/* storage class and scope */

#include <stdio.h>

 

void funct1(void);

void funct2(void);

 

/* external variable, scope is global to main(), funct1() and funct2(), extern keyword is omitted here, coz just one file */

int globvar = 10;

 

int main()

{

    printf("\n****storage classes and scope****\n");

    /* external variable */

    globvar = 20;

    

    printf("\nVariable globvar, in main() = %d\n", globvar);

    funct1();

    printf("\nVariable globvar, in main() = %d\n", globvar);

    funct2();

    printf("\nVariable globvar, in main() = %d\n", globvar);

    return 0;

}

 

/* external variable, scope is global to funct1() and funct2() */

int globvar2 = 30;

 

void funct1(void)

{

    /* auto variable, scope local to funct1() and funct1() cannot access the external globvar */

    char globvar;

   

    /* local variable to funct1() */

    globvar = 'A';

    /* external variable */

    globvar2 = 40;

   

    printf("\nIn funct1(), globvar = %c and globvar2 = %d\n", globvar, globvar2);

}

 

void funct2(void)

{

    /* auto variable, scope local to funct2(), and funct2() cannot access the external globvar2 */

    double globvar2;

    /* external variable */

    globvar =  50;

    /* auto local variable to funct2() */

    globvar2 = 1.234;

    printf("\nIn funct2(), globvar = %d and globvar2 = %.4f\n", globvar, globvar2);

}

 

Output:

 

C Storage class, scope and memory allocation

 Z.1.4  STATIC VARIABLE - static

static extern varx;

/* static storage class program example */

#include <stdio.h>

#define MAXNUM 3

 

void sum_up(void);

 

int main()

{

    int count;

    

    printf("\n*****static storage*****\n");

    printf("Key in 3 numbers to be summed ");

    for(count = 0; count < MAXNUM; count++)

        sum_up();

    printf("\n*****COMPLETED*****\n");

    return 0;

}

 

void sum_up(void)

{

    /* at compile time, sum is initialized to 0 */

    static int sum = 0;

    int num;

   

    printf("\nEnter a number: ");

    scanf("%d", &num);

    sum += num;

    printf("\nThe current total is: %d\n", sum);

}

 

Output:

 

C static storage class

Z.2  DYNAMIC MEMORY ALLOCATION

  • In the previous section we have described the storage classes which determined how memory for variables is allocated by the compiler.

  • When a variable is defined in the source program, the type of the variable determines how much memory the compiler allocates.

  • When the program executes, the variable consumes this amount of memory regardless of whether the program actually uses the memory allocated. This is particularly true for arrays.

  • However, in many situations, it is not clear how much memory the program will actually need.  For example, we may have declared arrays to be large enough to hold the maximum number of elements we expect our application to handle.

  • If too much memory is allocated and then not used, there is a waste of memory.  If not enough memory is allocated, the program is not able to fully handle the input data.

  • We can make our program more flexible if, during execution, it could allocate initial and additional memory when needed and free up the memory when it is no more needed.

  • Allocation of memory during execution is called dynamic memory allocation.  C provides library functions to allocate and free up memory dynamically during program execution.  Dynamic memory is allocated on the heap by the system.

  • It is important to realize that dynamic memory allocation also has limits.  If memory is repeatedly allocated, eventually the system will run out of memory.

 

Z.3  PROCESS MEMORY LAYOUT

C and process memory layout

 

Figure: z.1

 

Segment

Description

Code - text segment

Often referred to as the text segment, this is the area in which the executable or binary image instructions reside.  For example, Linux/Unix arranges things so that multiple running instances of the same program share their code if possible.  Only one copy of the instructions for the same program resides in memory at any time.  The portion of the executable file containing the text segment is the text section.

Initialized data – data segment

Statically allocated and global data that are initialized with nonzero values live in the data segment.  Each process running the same program has its own data segment.  The portion of the executable file containing the data segment is the data section.

Uninitialized data – bss segment

BSS stands for ‘Block Started by Symbol’.  Global and statically allocated data that initialized to zero by default are kept in what is called the BSS area of the process.  Each process running the same program has its own BSS area.  When running, the BSS, data are placed in the data segment.  In the executable file, they are stored in the BSS section.  For Linux/Unix the format of an executable, only variables that are initialized to a nonzero value occupy space in the executable’s disk file.

Heap

The heap is where dynamic memory (obtained by malloc(), calloc(), realloc() and new – C++) comes from.  Everything on a heap is anonymous, thus you can only access parts of it through a pointer. As memory is allocated on the heap, the process’s address space grows.  Although it is possible to give memory back to the system and shrink a process’s address space, this is almost never done because it will be allocated to other process again.   Freed memory (free() and delete – C++) goes back to the heap, creating what is called holes.   It is typical for the heap to grow upward.  This means that successive items that are added to the heap are added at addresses that are numerically greater than previous items.  It is also typical for the heap to start immediately after the BSS area of the data segment.  The end of the heap is marked by a pointer known as the break. You cannot reference past the break. You can, however, move the break pointer (via brk() and sbrk() system calls) to a new position to increase the amount of heap memory available.

Stack

The stack segment is where local (automatic) variables are allocated.  In C program, local variables are all variables declared inside the opening left curly brace of a function's body including the main() or other left curly brace that aren’t defined as static.  The data is popped up or pushed into the stack following the Last In First Out (LIFO) rule.  The stack holds local variables, temporary information, function parameters, return address and the like.  When a function is called, a stack frame (or a procedure activation record) is created and PUSHed onto the top of the stack. This stack frame contains information such as the address from which the function was called and where to jump back to when the function is finished (return address), parameters, local variables, and any other information needed by the invoked function. The order of the information may vary by system and compiler.  When a function returns, the stack frame is POPped from the stack.  Typically the stack grows downward, meaning that items deeper in the call chain are at numerically lower addresses and toward the heap.

 

Table z.1

 

Disk file segments

 

Figure z.2

 

Z.4  Some Terms

Memory Allocation

Type

Description

Static allocation

This allocation happens when you declare a static or global variable.  Each static or global variable defines one block of space, of a fixed size.  The space is allocated once, when your program is started, and is never freed.  In memory address space, for uninitialized variables are stored in bss segment while an initialized variables stored in data segment.

Automatic allocation

This allocation happens when you declare an automatic variable, such as a function argument or a local variable. The space for an automatic variable is allocated when the compound statement containing the declaration is entered, and is freed when that compound statement is exited.  As discussed before this allocation done in the stack segment.

 

Table z.2

Z.5  STACK AND HEAP

#include <stdio.h>

 

int a();

int b();

int c();

 

int a()

{

b();

c();

return 0;

}

 

int b()

{ return 0; }

 

int c()

{ return 0; }

 

int main()

{

a();

return 0;

}

Stack frame and function call

Figure z.3:  Stack frame and function call

 

Z.5.1  FUNCTION CALLING CONVENTION

  1. The order in which function arguments are pushed onto the stack.

  2. Whether the caller function or called function (callee) responsibility to remove the arguments from the stack at the end of the call that is the stack cleanup process.

  3. The name-decorating convention that the compiler uses to identify individual functions.

void __cdecl TestFunc(float a, char b, char c);   // Borland and Microsoft
void  TestFunc(float a, char b, char c)  __attribute__((cdecl));  // GNU GCC

keyword

Stack cleanup

Parameter passing

__cdecl

caller

Pushes parameters on the stack, in reverse order (right to left).  Caller cleans up the stack.  This is the default calling convention for C language that supports variadic functions (variable number of argument or type list such as printf()) and also C++ programs.  The cdecl calling convention creates larger executables than __stdcall, because it requires each function call (caller) to include stack cleanup code.

__stdcall

callee

Also known as __pascal.  Pushes parameters on the stack, in reverse order (right to left).  Functions that use this calling convention require a function prototype.  Callee cleans up the stack.  It is standard convention used in Win32 API functions.

__fastcall

callee

Parameters stored in registers, then pushed on stack.  The fastcall calling convention specifies that arguments to functions are to be passed in registers, when possible.  Callee cleans up the stack.

 

Table z.3:  Function calling conventions

/* example of __cdecl */

push   arg1

push   arg2

call      function

add     ebp, 12   ;stack cleanup

/* example of __stdcall */

push   arg1

push   arg2

call      function

/* no stack cleanup, it will be done by callee */

Z.5.2  DYNAMIC ALLOCATION – THE FUNCTIONS

Function

Prototype and Description

malloc()

void * malloc (size_t nbytes);

nbytes is the number of bytes that to be assigned to the pointer.  The function returns a pointer of type void*.  When allocating memory, malloc() returns a pointer which is just a byte address.  Thus, it does not point to an object of a specific type.  A pointer type that does not point to a specific data type is said to point to void type, that is why we have to type cast the value to the type of the destination pointer, for example:

 

char * test;

test = (char *) malloc(10);

 

This assigns test a pointer to a usable block of 10 bytes.

 

calloc()

void * calloc (size_t nelements, size_t size);

calloc() is very similar to malloc() in its operation except its prototype have two parameters.  These two parameters are multiplied to obtain the total size of the memory block to be assigned. Usually the first parameter (nelements) is the number of elements and the second one (size) serves to specify the size of each element. For example, we could define test with calloc():

 

int * test;

test = (int *) calloc(5, sizeof(int));

 

Another difference between malloc() and calloc() is that calloc() initializes all its elements to 0.

 

realloc()

void * realloc (void * pointer, size_t elemsize);

It changes the size of a memory block already assigned to a pointer.  pointer parameter receives a pointer to the already assigned memory block or a null pointer (if fail), and size specifies the new size that the memory block shall have.  The function assigns size bytes of memory to the pointer.  The function may need to change the location of the memory block so that the new size can fit; in that case the present content of the block is copied to the new one.  The new pointer is returned by the function and if it has not been possible to assign the memory block with the new size it returns a null pointer.

 

free()

void free (void * pointer);

It releases a block of dynamic memory previously assigned using malloc(), calloc() or realloc().  This function must only be used to release memory assigned with functions malloc(), calloc() and realloc().

 

NULL

NULL is a defined constant used to express null pointers, that is, an unassigned pointer (pointing to the address 0) or a pointer that points to something but not useful.

 

size_t

Defined type used as arguments for some functions that require sizes or counts specifications.  This represents an unsigned value generally defined in header files as unsigned int or by using typedef, typedef unsigned int size_t;

 

Table z.4

int     *theptr;
theptr = (int *)malloc(sizeof(int));
if (theptr != NULL)
*theptr = 23;
if (theptr)
*theptr = 23;
printf("Value stored is %d\n", *theptr);
free((void *) theptr);
free(theptr);

float *

fptr = (float *) malloc(200 * sizeof(float));

-----------------------------------o0o -----------------------------------

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Check the best selling C and C++ books at Amazon.com.

  2. C and buffer overflow tutorial: stack frame activity.

  3. C and Assembler, Compiler and Linker.

  4. C lab worksheet practice.

 

 

 

|< C & C++ Compiler, Assembler, Linker & Loader | Main | C Storage Class & Memory 2 >| C++/OOP | Site Index | Download |


 

C Storage Class & Memory:  Part 1 | Part 2 |