Storage Classes & gdb

There are many different types of variable storage classes and memory usages in C and C++. Here is an overview, along with basic usage of gdb for debugging our programs.

Using gdb for debugging

See the posted tutorials and cheat sheet. Here are some basics:

  1. Compile using -g to add debug info: gcc -g -o program_name my_c_code.c
  2. Running: gdb program_name
  3. You are now in GDB with prompt (gdb); type break main:1 to set a breakpoint at line 1 of main()
  4. Type run to run the program
  5. Now, typing next (or just n) will run through a line of code if you set a breakpoint, skipping over function calls
  6. Similarly, typing step (or just s) will execute statement by statement, including into function calls
  7. Also, if you type just a return/enter key, gdb will repeat the previous command, letting you step through statements quickly
  8. Typing print i+1 will print value of int variable i, plus one

Storage classes/variable types

Recall that lifetime and scope of a variable are not always the same. Lifetime is how long the variable stays alive (persists in memory) at runtime. Scope is what parts of code can see the definition and access the variable.

Different types of storage classes:

Static variables

The static qualifier can be used with any type of variable declaration, e.g. static int intarray[10];

The program below provides examples of several storage types, and also what happens when a local variable and a global variable have the same name.

#include <stdio.h>

// a global variable
int v = 100;

// void f();
void f1(void);
void f2(int);

int main(void) {
    // f(3, 5); compiles if the forward declaration were f()
    f1();

    f2(10);

    printf("global v hasn't changed\n");
    printf(" val: %d\n", v);          // we can access the global here
    printf(" loc: %p\n", (void*)&v);  // where global v lives
    printf("size: %lu\n", sizeof(v)); // v has a size (in bytes)
    printf("\n");

    return 0;

}

void f2(int a) {
    printf("a: %d\n", a);
    f1();
}

void f1(void) {
    static int i;  // only created & initialized once, first time function is called
    printf("static i: %d\n", ++i);    // static variable has longer life

    printf("access global v\n");
    printf(" val: %d\n", v);          // we can access the global here
    printf(" loc: %p\n", (void*)&v);  // where global v lives
    printf("size: %lu\n", sizeof(v)); // v has a size (in bytes)
    printf("\n");
 
    // declare a variable in a different scope but with the same name
    double v = 45.0;
    printf("local v 'shadows' global v\n");
    printf(" val: %f\n", v);          // local v "shadows" global v
    printf(" loc: %p\n", (void*)&v);  // local v has its own address
    printf("size: %lu\n", sizeof(v)); // and size
    printf("\n");
}

const

Indicates data is constant, the value cannot change after initialization.

const can be used at different points in more complex types, with different meanings

Memory segments

During execution our program variables are stored in different sections of memory, depending on their declaration type.