DEV Community

Comfort Ajala
Comfort Ajala

Posted on

I am learning C in 2020 – Beginner - What I learned about memory, variables and pointers in c

Original Post : HERE
Book: Head First C

Chapter: 3

Platform used: cplayground.com

Pointers humbled me

Problem

Goal

Understand pointers at a foundational level.

Memory Zones in C

I figured it would be useful to gain a simple overview of where variables could live during the run of a c program.

Stack

Variables declared or/and initialised within a function are said to live the stack memory. Note that once the function is called and completes its task, all the local variables cease to exist in the stack memory. Based on several articles I have read and listed below, each local variable ( within the scope of a function) is pushed into the stack memory. Although the stack variables are pushed into the stack memory ( downward growth of stack or in a last-in-first-out manner ), they are removed via a POP function. This denotes the organized process in stack variable management.

Heap

Unlike stack variables, heap variables are allocated in a more disorganized manner. Heap variables are created dynamically by using the inbuilt malloc function and removed or their memory allocation freed by the free function. To access heap variables, pointers are used.A really cool thing about heap variables is that since they are dynamically allocated, they can be accessed outside the context of a function using their pointers.

Static

These are variables that retain their value even after the function is complete. Additionally after the first initialisation of a declared static variable, subsequent initialisations in the same scope/ function are ignored. Keep in mind that uninitialized static variables are set to 0; The code snippet below demonstrates this:

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


void foo(){
    
    static int num = 5; // initialized 
    printf("The value of the static local NUM in foo() is %d\n", num);
    num++;
}

void doo(){
    int num = 5; // initialized 
    printf("The value of the local NUM in doo() is %d\n", num);
    num++;
}

void moo(){
    static int num; // uninitialized 
    printf("The value of the static local NUM in moo() is %d\n", num);
    num++;
}
int main() {
        
        for(int i; i < 5; i++){
            foo();
            doo();
            moo();
printf("...................................................\n\n");
        }
    return 0;

}

Run…

The value of the static local NUM in foo() is 5
The value of the local NUM in doo() is 5
The value of the static local NUM in moo() is 0
...................................................

The value of the static local NUM in foo() is 6
The value of the local NUM in doo() is 5
The value of the static local NUM in moo() is 1
...................................................

The value of the static local NUM in foo() is 7
The value of the local NUM in doo() is 5
The value of the static local NUM in moo() is 2
...................................................

The value of the static local NUM in foo() is 8
The value of the local NUM in doo() is 5
The value of the static local NUM in moo() is 3
...................................................

The value of the static local NUM in foo() is 9
The value of the local NUM in doo() is 5
The value of the static local NUM in moo() is 4
...................................................

However static and global variables neither live in the stack nor in the heap memory. They are said to be stored in another area.

Text Segment

Also known as the code segment, is the portion of a program where the executables or bytecode instructions of the program lives. Constants also reside in this memory.

Data Segment

Initialized data segment

Here exist all the global and static variables ( global and local statics) of a program. This segment is not read-only, meaning variables stored here can be re-initialized or assigned new values.

Uninitialized data segment

Also called the bss segment, holds variables that were not given values by the programmer and consequently all set to zero.

To be frank, I can't say I fully grasp these concepts as I am new to these, but hopefully it helps you a bit.

Exercise

Where are the following variables stored ?

#include <stdio.h>
#include <stdlib.h>
int globalInt;
int intializedInt = 23;
static int initializedStaticInt = 11;
int main() {
   static int staticInt = 5;
    int unIntializedInt;
    int val  = 3;
    char name[] = "Comfort Ajala";
    char list[32];
    char initList[32] =  {0}; 
    char * allocatedforchar = (char * ) malloc(20* sizeof(char));
    char *lastname = "Ajala";
    return 0;

}

My Solution

globalInt → an uninitialized global variable → BSS

intializedInt → initialized global variable → Initialized data segment

initializedStaticInt → initialized global static variable → Initialized data segment

staticInt → initialized static local variable → Initialized data segment

unIntializedInt → uninitialized local variable → stack

“Only variables with static storage duration end up in .bss and .data. Local variables always end up on the stack, or in CPU registers, no matter if they are initialized or not.” - Lundin

Val → initialized local variable → stack

Name → initialized local variable → stack

List → uninitialized local variable ( static array ) → stack

initList → initialized local variable → stack

Allocatedforchar → initialized local variable ( dynamic array ) → heap

Lastname → initialized local variable → stack

The string literal does not live in the stack though, it is stored in read-only memory. Read here for more.

Variable

Which can be of type integer ( int), float, char etc stores of a value of the designated type at a specific address.

Memory address

According to this article, memory is a place where variables are stored while a program is running. I am curious about what happens when the program stops. Does the memory disappear ?

The machine memory is apparently a list of bytes, where each byte,a collection of 8 bits, is a unique address. However depending on the type of data stored, a single byte or a group of bytes may be used.This means, the number of bytes used to store an integer or a long value may differ from that used to store a character. It is also important to keep in mind that the size or number of bytes for storage may also vary based on operating systems/ platforms. The address of a given variable, which is also referred to as a pointer, can be accessed by using &.

Variable type Size(byte) Size (bit)
int 2 or 4 bytes 16 or 32 bits
long 8 bytes 64 bits
char 1 byte 8 bits

Example…

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

int main() {
    size_t sizeofachar = 10 * sizeof(char);
    printf("size of char %li\n How many bytes will be allocated for 10 characters = %li\n", sizeof(char), sizeofachar);
    
    
     size_t sizeofint = 10 * sizeof(int);
    printf("size of int %li\n How many bytes will be allocated for 10 integers  = %li\n", sizeof(int), sizeofint);
    
    
     size_t sizeoffloat = 10 * sizeof(float);
    printf("size of float %li\n How many bytes will be allocated for 10 floats = %li\n", sizeof(float), sizeoffloat);
    
    //%li --> signed integer
    return 0;
}

Result

size of char 1
 How many bytes will be allocated for 10 characters = 10
size of int 4
 How many bytes will be allocated for 10 integers  = 40
size of float 4
 How many bytes will be allocated for 10 floats = 40

Zum Beispiel…

When you create a variable of type int and give it a value, the computer looks at the type, determines how many bytes are required for that type based on the OS, selects the byte or bytes, throws the value in and locks the door. Our value is stored and secured.

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


int main(){
    
    int integer = 3;
    printf("The variable 'integer' is stored at address %p\n", &integer);
    printf("The variable holds value %i\n", integer);
    return 0;
}

When ran, looks like this:

C:\Users\....\practice\c> & cmd.exe /c "gcc practice.c -o a && a"  
The variable 'integer' is stored at address 0061FF1C
The variable holds value 3

Indirection operator

This unary operator (*),when used on a pointer, helps us access the value that is pointed at by the pointer. Whew… so many points.

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

int main() {
    int favnumber = 6;
    int * ptr = &favnumber;
    printf("What value does the variable ptr hold ? = %p\n", ptr);
    printf("What value does the variable ptr point to ? = %d\n", * ptr);
    printf("Is the pointer's home address, %p, identical to that of the favnumber's address, %p, ?\n", &ptr, ptr);
    //%p --> format specifier for address of pointers or any variable (void *)
    //%d ---> informs printf to treat the incoming value as an integer
    return 0;
}

Result…

What value does the variable ptr hold ? = 0x7ffe30cb607c
What value does the variable ptr point to ? = 6
Is the pointer's home address, 0x7ffe30cb6080, identical to that of the favnumber's address, 0x7ffe30cb607c, ?

The “char”

char character = 'n';

Where in memory does the character variable live ?

If declared in a function → stack

If declared outside a function → global → data segment

The “char” pointer variable

char *firstchar = 'c';
char *message = "every good boy does fine";

Where in memory do these variables live ?

If declared in a function → stack

If declared outside a function → global → data segment

Where do each pointer point to ?

Let’s log everythang..

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

int main() {
  char *firstchar = 'c';
char *message = "every good boy does fine";
printf("*firstchar points to %p\n", firstchar);
printf("*message points to %p while the first character of the constant string lives in %p\n", message, &message[0]);
    return 0;

}
                   Compiled in 192.949 ms                   
                        Executing...                        
*firstchar points to 0x63
*message points to 0x4005c8 while the first character of the constant string lives in 0x4005c8

As you can see, these pointers point to the first character of the given string

message[0] = 'm';
*message[0] = 'm';
*(&message[0]) = 'm';

Why would the above expression not work?

Because the pointer points to a string literal, an unmodifiable value. I was a bit confused by the pointer myself, since I always associated POINTERS with MODIFIABLE. But apparently this pointer here only means one can bind the pointer to another string literal. For instance :

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

int main() {
  char * message  = "every good boy does fine";
  printf("1. Where does message point to <%s> at address %p\n", message, message);
  message = "Just another string";
  printf("2. Where does message point to <%s> at address %p\n", message, message);
    return 0;

}
1. Where does message point to <every good boy does fine> at address 0x4005d8
2. Where does message point to <Just another string> at address 0x40062b

How does this pointer know the length of the string literal?

I assume it uses '\0' or NULL characters to determine the end of the string. Read here

The char array

char notes[] = "every good boy does fine";

Where do notes live in memory?

If declared in a function → stack

If declared outside a function → global → data segment

Here the program allocates 25 bytes ( 1 char → 1 byte ) for the 24 characters and the terminating character.

notes[0] = 'E';

Why can I change the character at index 0 of notes but not do the same in the message pointer variable above ?

Because string literals are stored in the read-only area of memory, while the notes variable, since initialised, is stored in the read-write region. If declared and initialised in a function, then it is stored in the stack, else most likely in the initialised data segment.

*(notes[0]) = 'W';

Notes[0] → returns a char not an address

The unary operator expects an address, so this would break.

Why will the above not work ?

*(&notes[0]) = 'W';

Why will the above work?

Notes[0] → returns a char not an address

&notes[0] → returns the memory location (address) of the char at index 0

Since the indirection operator expects and gets an address, this would work.

Wvery good boy does fine

The “Int”

int num = 4;

Where does num variable live ?

If declared in a function → stack

If declared outside a function → global → data segment

Pointer variable to an integer

int *numptr = #

Array of integers

int var[5] = {1, 2, 3, 4};

Does each integer in the var array live 2 or 4 byte blocks away from each other ?

Let’s test this, shall we….

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

int main() {

int var[5] = {1, 2, 3, 4};
int * address_of_first_int = &var[0];
for(int i = 0; i < 5; i++){
    
    printf("Address at <%d> = <%p>; <%d * %li> bytes from the first int address <%p>\n", *(var + i), &var[i],i, sizeof(int), (address_of_first_int + i));
}
    return 0;

}
Address at <1> = <0x7ffe1d81e020>; <0 * 4> bytes from the first int address <0x7ffe1d81e020>
Address at <2> = <0x7ffe1d81e024>; <1 * 4> bytes from the first int address <0x7ffe1d81e024>
Address at <3> = <0x7ffe1d81e028>; <2 * 4> bytes from the first int address <0x7ffe1d81e028>
Address at <4> = <0x7ffe1d81e02c>; <3 * 4> bytes from the first int address <0x7ffe1d81e02c>
Address at <0> = <0x7ffe1d81e030>; <4 * 4> bytes from the first int address <0x7ffe1d81e030>

It is safe to say that the answer is yes.

Read up pointer arithmetics

Is an array a pointer ?

Ja und nein.depending on how it is used.

A pointer to an int array → points to the memory location of first element of the array → does not hold values of the array

An int array → holds N integers → when its identifier is assigned to a pointer, it changes to the pointer of the first element

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

int main() {

int var[5] = {1, 2, 3, 4};
int * address_of_first_int = &var[0];
address_of_first_int[3] = 12;
printf("Value at %d is %d", 3, var[3]);
    return 0; 

}

Why does the above work though ?

According to a stack overflow response, the symbol [] works with pointers. The integer within the symbol is used as the offset value from the first element of the array and the value residing is returned. This indicates that the “var” variable has to be converted to a pointer, before a value at a specific index can be set or returned.

int *ptr = var;
int *secondptr = &var[0];

What is the difference between the two pointers ?

Nada = Nothing = Nichts

const int ARRAY_SIZE = 5;
int(*wholeptr)[ARRAY_SIZE] = &var;

WTH is that ????

I fell into this randomly in the GeeksforGeeks blog and I thought it would also be nice here :D. It is a pointer to an array. I think I would need to create another blog post for this alone because I honestly do not get it.

int *fiveintptr[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; i++)
    {
        /* code */
        fiveintptr[i] = &var[i];
    }

Before the loop, where do each pointer in the fiveintptr pointer array point to ?

The fiveintptr variable is an array of pointers with each pointer expected to point to an int type. But since each pointer isn't assigned a value / address, it holds garbage values.

#include <stdio.h>
#include <stdlib.h>
const int ARRAY_SIZE = 5;
int main() {

int var[5] = {1, 2, 3, 4};
int *fiveintptr[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; i++)
    {
        /* code */
        printf("The value pointed to by the pointer at index %d is %p\n", i,  fiveintptr[i]);
    }
    
    puts("----------------------------------------------\n");
    for (int i = 0; i < ARRAY_SIZE; i++)
    {
        /* code */
        fiveintptr[i] = &var[i];
        printf("The value pointed to by the pointer at index %d is %p\n", i,  fiveintptr[i]);
    }

    
    return 0; 

}
The value pointed to by the pointer at index 0 is (nil)
The value pointed to by the pointer at index 1 is (nil)
The value pointed to by the pointer at index 2 is (nil)
The value pointed to by the pointer at index 3 is 0x756e6547
The value pointed to by the pointer at index 4 is 0x9
----------------------------------------------

The value pointed to by the pointer at index 0 is 0x7ffd70e07df0
The value pointed to by the pointer at index 1 is 0x7ffd70e07df4
The value pointed to by the pointer at index 2 is 0x7ffd70e07df8
The value pointed to by the pointer at index 3 is 0x7ffd70e07dfc
The value pointed to by the pointer at index 4 is 0x7ffd70e07e00
*fiveintptr[4] = 25;

Why would it work?

Fiveintptr → array of pointer

Fiveintptr[4] ---> a pointer

*pointer → dereferencing a pointer → value at given address / pointer

*fiveintptr[4] → value living at an address of 3rd element of the var int array

= → basic assignment operator

// valid operation

The array of strings

char top5femalartists[5][20] = {
        "beyonce",
        "jessie j",
        "christina aguilera",
        "maria carey",
        "mary j blige"
};

Do you agree with my list ?

yesssss!

What do the 5 and 20 mean ?

5 → number of char arrays that can be stored here

20 → number of characters that can be stored in each char array

What happens when a string like "beyonce" is shorter than 20?

The remaining bytes are either empty or filled up with jargon. For example…

#include <stdio.h>
#include <stdlib.h>
int main() {

 char person[20] = "beyonce";
 printf("Value at 6 %c <>\n", person[6]);
 printf("Value at 7 %c<>\n", person[7]);
printf("Value at 8 %c<>\n", person[8]);
    return 0; 

}
Value at 6 e <>
Value at 7 <>
Value at 8 <>
top5femalartists[1] = "jennifer hudson";

Why would the above expression not work ?

error: assignment to expression with array type

Because they are not identical. “Beyonce” is an array of chars ( 20 ), while "jennifer hudson" is stored in read-only memory. They are of different types.

char *top5maleartist[5] = {
        "bruno mars",
        "michael jackson"
};

What does this look like ?

An array of pointers, each pointing to string literals.

top5maleartist[0] = "DO of exo";

Why did the above work ?

You simple rebound the pointer to another string literal

top5maleartist[0][1] = 'd';

Why wouldn't the above work ?

You are trying to modify the second character of the string literal pointed by the first pointer. Remember this value type is treated as read-only, which is said to have an “undefined” behaviour.

Got the last part from stack overflow :D

In what way could I make this modifiable ?

Array of chars.

#include <stdio.h>
#include <stdlib.h>
int main() {


char * top5maleartist[3];
char ch_arr[3][20] = {
                        "bruno mars",
        "michael jackson"
                     };
                     
for(int i = 0; i < 3; i++){
    
    top5maleartist[i] = &ch_arr[i][0];
}

*(top5maleartist[0] + 1) = 'd';
printf("Value of %s\n", (top5maleartist[0]));
    return 0; 

}
Run.....
Value of bduno mars
  1. Create an array of pointers containing 3 pointers
  2. Create an array of an array of characters
  3. Set each pointer to the address of the first character of each char list

Top5maleartist[0] → pointer to ch_arr[0][0]

Top5maleartist[0] + 1 → shift by char characters * 1 → &ch_arr[0][1]

*(Top5maleartist[0] + 1 ) → *(&ch_arr[0][1]) → dereferencing

What happens to the string literal “bruno mars”?

I dunno.. .seriously.

The double pointer

int num = 3;
int  *numptr = #
int **doubleptr = &numptr;
printf("%d", **doubleptr);

What value does it dereference ?

*doubleptr → &num

**doubleptr → *(&num) → 3

printf("%d\n", *numptr);
printf("%d\n", **doubleptr);
printf("%p\n", *doubleptr);

What gets logged ?

3

3

0x7fff685046f4

Author Notes

This is most likely not the cleanest implementation, just a heads up.

Links

  1. MEMORY IN C – THE STACK, THE HEAP, AND STATIC
  2. Memory : Stack vs Heap
  3. Stack Memory Operations
  4. What is a Memory Heap?
  5. Pointers in C and C++ | Set 1 (Introduction, Arithmetic and Array)
  6. Memory Layout of C Programs
  7. Basics of Memory Addresses in C
  8. Memory addresses wonderfully explained
  9. C - Data Types
  10. The Memory and Memory Addresses
  11. What’s difference between char s[] and char *s in C?
  12. Array of Strings in C
  13. char *array and char array[]
  14. Array pointers
  15. Pointer vs Array in C
  16. Array of pointers
  17. Array of pointers to strings
  18. Double pointers
  19. %li
  20. Size_int
  21. Where global variables are stored?
  22. Rationale behind code segment and data segment
  23. Storage of global and static variables in C
  24. Executable
  25. Code segment
  26. Data segment
  27. C Strings (Arrays vs. Pointers)
  28. Static variable inside of a function in C
  29. Where are static variables stored in C/C++?
  30. difference between stack segment and uninitialized data segment
  31. Location of pointers and global variables in C
  32. Array Memory Allocation in C Programming
  33. Is an array name a pointer?
  34. Pointer to an Array | Array Pointer
  35. Difference between pointer to an array and array of pointers
  36. String literals: Where do they go?

Top comments (0)