C Constants and Initialization: The Unchangeable Rules
The Story of the Stubborn Sign
Imagine you own a lemonade stand. You paint a big sign that says “LEMONADE - $1”.
This sign is permanent. You can’t change it every five minutes. Everyone who walks by sees the same price. That’s what a constant is in C — a value that stays the same forever!
What Are Constants and Literals?
Literals: The Actual Values
A literal is just a value you type directly into your code.
5 // Integer literal
3.14 // Float literal
'A' // Character literal
"Hello" // String literal
Think of literals like writing “$1” on your sign. It’s the actual value you see!
Constants: Values That Never Change
A constant is a named value that can’t be modified after it’s set.
const int PRICE = 1;
// PRICE will ALWAYS be 1
// You can't change it later!
Why use constants?
- Prevent accidental changes
- Make code easier to read
- Change one place, update everywhere
The const Keyword
The const keyword tells C: “This value is read-only. Don’t let anyone change it!”
Basic Example
const int MAX_PLAYERS = 4;
const float PI = 3.14159;
const char GRADE = 'A';
What Happens If You Try to Change It?
const int age = 10;
age = 11; // ERROR! Can't change!
The compiler will stop you. It’s like trying to erase permanent marker!
const with Pointers (A Bit Tricky!)
const int *ptr; // Can't change VALUE
int *const ptr; // Can't change ADDRESS
const int *const p; // Can't change EITHER!
Simple trick:
- Read right to left
constbefore*= constant valueconstafter*= constant pointer
The #define Directive
#define is like a find-and-replace before your code runs.
How It Works
#define MAX_SIZE 100
#define PI 3.14159
#define GREETING "Hello!"
When you write:
int arr[MAX_SIZE];
The compiler sees:
int arr[100];
No Equals Sign! No Semicolon!
#define VALUE 50 // Correct!
#define VALUE = 50 // WRONG!
#define VALUE 50; // WRONG!
Macros with Parameters
#define SQUARE(x) ((x) * (x))
#define MAX(a,b) ((a)>(b)?(a):(b))
int result = SQUARE(5); // Becomes 25
int bigger = MAX(3, 7); // Becomes 7
Warning: Always use parentheses! Without them:
#define SQUARE(x) x * x
SQUARE(2+3) // Becomes 2+3*2+3 = 11
// NOT 25!
const vs #define: The Showdown
| Feature | const |
#define |
|---|---|---|
| Type checking | Yes | No |
| Memory used | Yes | No |
| Debugging | Easy | Hard |
| Scope | Limited | Global |
| When processed | Compile time | Before compile |
When to Use Each?
Use const when:
- You need type safety
- You want easier debugging
- Working with arrays or pointers
Use #define when:
- Defining array sizes
- Creating macros with parameters
- Conditional compilation
Example Comparison
// With const
const int SIZE = 10;
int arr[SIZE]; // May not work in C89!
// With #define
#define SIZE 10
int arr[SIZE]; // Always works!
L-value and R-value: Left and Right
Think of the equals sign = like a door:
- L-value = What’s on the LEFT (the container)
- R-value = What’s on the RIGHT (the content)
Simple Example
int x = 5;
// ^ ^
// L R
// box value
xis an L-value (it has an address in memory)5is an R-value (just a value)
L-value: Must Have an Address
int a;
a = 10; // OK! 'a' is L-value
10 = a; // ERROR! 10 is not L-value
(a+1) = 5; // ERROR! Expression not L-value
R-value: Can Be Anything
int x = 5; // 5 is R-value
int y = x; // x becomes R-value here
int z = x + y; // (x + y) is R-value
The Simple Rule
L-value = "I can hold something" (has address)
R-value = "I am something" (just a value)
graph TD A[Assignment: x = 5] --> B[L-value: x] A --> C[R-value: 5] B --> D[Has memory address] B --> E[Can store values] C --> F[Just a value] C --> G[No permanent address]
Declaration vs Definition
These two words confuse everyone! Let’s clear it up.
Declaration: “Hey, this exists!”
A declaration tells the compiler:
“Trust me, this thing exists somewhere.”
extern int count; // Declaration
int add(int a, int b); // Declaration
No memory is allocated. Just a promise!
Definition: “Here it is!”
A definition actually creates the thing and gives it memory.
int count = 0; // Definition
int add(int a, int b) { // Definition
return a + b;
}
The Simple Analogy
| Declaration | Definition |
|---|---|
| “I have a dog” | Shows the actual dog |
| Reservation | Actual delivery |
| Movie trailer | The movie itself |
Key Rules
// Declaration only (no memory)
extern int x;
// Definition (allocates memory)
int x;
int x = 10;
// Both declaration AND definition
int y = 5;
Multiple Declarations, One Definition
// file1.c
int global_var = 100; // Definition
// file2.c
extern int global_var; // Declaration
// Uses the same variable!
graph LR A[Declaration] --> B[Tells compiler it exists] A --> C[No memory allocated] D[Definition] --> E[Creates the thing] D --> F[Allocates memory] G[Rule] --> H[Many declarations allowed] G --> I[Only ONE definition!]
Quick Summary
| Concept | What It Does | Example |
|---|---|---|
| Literal | Raw value in code | 42, 'A', "Hi" |
const |
Type-safe constant | const int X = 5; |
#define |
Text replacement | #define X 5 |
| L-value | Can receive value | x in x = 5 |
| R-value | Provides value | 5 in x = 5 |
| Declaration | Announces existence | extern int x; |
| Definition | Creates & allocates | int x = 10; |
Your Superpower Unlocked!
You now understand:
- How to create values that never change
- The difference between
constand#define - Why some things go on the left, some on the right
- The secret handshake between declaration and definition
These aren’t just rules. They’re the foundation of writing safe, clean, professional C code.
Go build something awesome!