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 ?
*(¬es[0]) = 'W';
Why will the above work?
Notes[0] → returns a char not an address
¬es[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
- Create an array of pointers containing 3 pointers
- Create an array of an array of characters
- 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
- MEMORY IN C – THE STACK, THE HEAP, AND STATIC
- Memory : Stack vs Heap
- Stack Memory Operations
- What is a Memory Heap?
- Pointers in C and C++ | Set 1 (Introduction, Arithmetic and Array)
- Memory Layout of C Programs
- Basics of Memory Addresses in C
- Memory addresses wonderfully explained
- C - Data Types
- The Memory and Memory Addresses
- What’s difference between char s[] and char *s in C?
- Array of Strings in C
- char *array and char array[]
- Array pointers
- Pointer vs Array in C
- Array of pointers
- Array of pointers to strings
- Double pointers
- %li
- Size_int
- Where global variables are stored?
- Rationale behind code segment and data segment
- Storage of global and static variables in C
- Executable
- Code segment
- Data segment
- C Strings (Arrays vs. Pointers)
- Static variable inside of a function in C
- Where are static variables stored in C/C++?
- difference between stack segment and uninitialized data segment
- Location of pointers and global variables in C
- Array Memory Allocation in C Programming
- Is an array name a pointer?
- Pointer to an Array | Array Pointer
- Difference between pointer to an array and array of pointers
- String literals: Where do they go?
Top comments (0)