The above figure represents 4 memory segmentations.
Why Learn C?
C is one of the oldest, most foundational programming languages. Learning it will not only sharpen your programming skills but also give you a better understanding of how computers really work under the hood—especially when it comes to managing memory. It’s like learning how to drive a manual car before jumping into an automatic!
The Famous "Hello, World!"
Let’s dive in by writing the classic "Hello, World!" program. Every coder starts here, so let’s not break tradition!
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
What’s going on here?
- #include <stdio.h>: This is like importing a library. We're saying, "Hey, we need the tools from the
stdio.h
library," which lets us do input/output stuff (like printing to the screen). - int main(): This is where our program starts. The
main
function is the heart of every C program. - printf("Hello, World!\n");: This prints "Hello, World!" to our screen. The
\n
is a special character that means "new line," so it moves the cursor to the next line after printing. - return 0;: This tells the computer that the program ran successfully.
Variables: Storing Your Stuff
Variables are like little containers that hold information for us. In C, we have to declare the type of data our variable will hold.
int age = 25;
float height = 5.9;
char grade = 'A';
Here’s the breakdown:
- int: Stores integers (whole numbers), like 25 or 100.
- float: Stores decimal numbers, like 5.9.
- char: Stores a single character, like 'A' or 'B'. Don’t forget to use single quotes for characters! Double quotes for strings!
Memory Segmentation in C: How the Program Stores Data
Now, let’s take a quick look at how and where your program’s data is stored in memory. When a C program runs, memory is divided into several segments. Understanding these segments can give you insights into how C handles variables, functions, and pointers. You can refer to the top image above.
Here’s a high-level view of the memory segmentation:
- Text (Code) Segment
- Data Segment
- BSS (Block Started by Symbol) Segment
- Heap
- Stack
1. Text (Code) Segment
This segment holds the compiled code of our program, i.e., the actual machine instructions. It’s usually read-only, which means we can’t modify the code while the program is running.
In the "Hello, World!" program, the code inside main()
and printf()
functions is stored here.
2. Data Segment
The Data Segment is used for storing initialized global and static variables. If we declare any global or static variables with an initial value, they are stored here. This memory is allocated before the program runs and remains there until the program ends.
Example:
int globalVar = 10; // Stored in Data Segment
3. BSS Segment
The BSS (Block Started by Symbol) Segment is similar to the Data Segment but for uninitialized global and static variables. Variables that don’t have an initial value are stored here. The system automatically initializes them to zero when our program starts.
Example:
int uninitializedGlobal; // Stored in BSS Segment (auto-initialized to 0)
4. Heap Segment
The Heap is where dynamically allocated memory lives. When we use functions like malloc()
or calloc()
to allocate memory during runtime, it’s taken from the heap. We have to manually free this memory with free()
once we're done with it, otherwise we might get memory leaks. Okay, for those who aren't familiar with the term memory leaks: check this:
Imagine you’re hosting a party and you give out balloons to your guests. Each guest gets one, but when they leave, they’re supposed to give the balloon back so you can reuse it. Now, if some guests walk out with the balloons and never return them, you eventually run out of balloons, right? That’s kinda like what happens with memory leaks in programming!
In C, when we dynamically allocate memory using malloc()
(or similar functions), it’s like giving out a "balloon" (memory). But if we forget to return the balloon using free()
, that memory stays allocated and can’t be reused—even though our program doesn’t need it anymore. This is what we call a memory leak.
If this happens a lot during our program’s runtime, our system starts running out of available memory, and things can get slow, or worse, crash!
So, the golden rule is: when you use malloc()
(allocate memory), always pair it with free()
(release the memory) when you’re done. That way, no memory gets "leaked" and your system stays healthy!
In short: A memory leak is when our program forgets to return memory it no longer needs, leading to wasted resources.
After freeing, we have to set the pointer variable to point to NULL to avoid dangling pointers. (a pointer that’s still holding an address to memory that has already been freed or deleted, making it unsafe to use.)
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int)); // Memory allocated in Heap
*ptr = 20;
printf("Value: %d\n", *ptr);
free(ptr); // Free the allocated memory
ptr = NULL; // to avoid dangling pointer
return 0;
}
5. Stack Segment
The Stack is used for storing local variables and function call information. When we call a function, its local variables are stored on the stack, and when the function ends, the space is freed. The stack grows and shrinks as functions are called and return.
Example:
int main() {
int localVar = 5; // Stored in Stack Segment
return 0;
}
The stack is also used to keep track of function calls, parameters, and return addresses. It’s fast but limited in size, and if we overuse it (for example, by calling too many functions or creating huge local arrays), we might run into a stack overflow.
If Statements: Making Decisions
Life is full of decisions, and so is programming. In C, we can use if
statements to make choices.
#include <stdio.h>
int main() {
int age = 20;
if (age >= 18) {
printf("You're an adult.\n");
} else {
printf("You're a minor.\n");
}
return 0;
}
If the condition in the if
statement is true (in this case, if age is 18 or older), the first block runs. If not, the else
block runs.
Loops: Doing Things Over and Over
Sometimes we need to do the same thing multiple times, and instead of writing the same code repeatedly, we can use loops!
For Loop:
#include <stdio.h>
int main() {
for (int i = 0; i < 5; i++) {
printf("This is loop number %d\n", i);
}
return 0;
}
- The
for
loop here runs five times.i
starts at 0, and the loop continues untili
reaches 5. Thei++
increases the value ofi
by 1 after each loop.
While Loop:
#include <stdio.h>
int main() {
int i = 0;
while (i < 5) {
printf("This is loop number %d\n", i);
i++;
}
return 0;
}
A while
loop keeps going as long as the condition (here, i < 5
) is true.
Functions: Reusing Code
Functions let us group up code and reuse it whenever we need it. They help keep our code organized and avoid repetition.
Simple Function Example:
#include <stdio.h>
void sayHello() {
printf("Hello from a function!\n");
}
int main() {
sayHello();
sayHello();
return 0;
}
This will print "Hello from a function!" twice. The sayHello()
function groups the code we want to reuse, and we can call it as many times as we like.
Wrapping Up
Learning C is a great way to understand how things work at a lower level, and as we dive deeper, we’ll get a clearer picture of how powerful it really is. Happy coding! 😊
Click on the button "Github" below to learn more about C. I have posted my learning notes on my github. I have extracted key points and concepts from the books I have read about C. Feel free to explore. Cheers!