Variables, Basic Types, and Keywords: How C Programs Name Memory
A variable in C is not an abstract box. It is a name bound to a specific block of storage. Types tell the compiler how large this storage is, how to interpret its bits, and what operations are valid on it. Keywords are reserved words used by the language to express control flow, types, storage duration, and qualifiers. If you enter this learning phase believing a variable is merely a "slot for values," your mental model will fracture when you encounter pointers, structs, and memory layout.
Variable Declarations
A declaration introduces a name into the program and specifies its type.
int age = 18;
double price = 99.5;
char grade = 'A';
These three lines declare three objects respectively:
ageis anint.priceis adouble.gradeis achar.
The variable name exists at the source code level. At runtime, what truly exists is object storage.
Declaration vs. Initialization
You can declare a variable without initializing it. However, an uninitialized automatic variable holds an indeterminate value; reading it results in undefined behavior or garbage logic.
int x; // Declaration, uninitialized
int y = 0; // Declaration and initialization
For beginners, it is highly recommended to always initialize local variables. This is not a conservative stylistic choice, but a fundamental habit to avoid undefined behavior.
Basic Integer Types
C provides multiple integer types. Their sizes and ranges are platform-dependent, but they adhere to basic ordering constraints.
| Type | Common Use Case |
|---|---|
char |
Characters or small integers |
short |
Smaller integers |
int |
Default integer size |
long |
Larger integers |
long long |
Integers guaranteed to be at least 64 bits |
Do not assume an int is always 32 bits.
When you need fixed-width integers, use <stdint.h>.
#include <stdint.h>
int32_t count = 0;
uint64_t total = 0;
Fixed-width types make file formats, network protocols, and cross-platform data reliable.
Signed vs. Unsigned
signed indicates a signed integer (positive, negative, or zero).
unsigned indicates an unsigned integer (non-negative only).
signed int a = -1;
unsigned int b = 1u;
Unsigned types are suited for bitwise operations and explicitly non-negative binary representations. However, they are not simply "safer positive integers." Mixing signed and unsigned types in operations can trigger implicit conversions that break your logic.
int x = -1;
unsigned int y = 1;
if (x < y) {
/* The result here may defy your intuition */
}
Avoid this type of mixed code. Boundary checks and length calculations require particularly careful auditing.
Floating-Point Types
Common floating-point types in C:
| Type | Meaning |
|---|---|
float |
Single precision |
double |
Double precision |
long double |
Extended precision, platform-dependent |
Floating-point numbers are not exact models of decimal fractions. They use binary approximations.
double x = 0.1 + 0.2;
x will not be precisely equal to 0.3.
Never rely directly on standard equality comparisons for floating-point numbers when handling currency, financial accounts, or authorization thresholds.
Character Types
char is used to store character encoding units or small integers.
char c = 'A';
'A' is a character constant.
Strings use double quotes:
char text[] = "hello";
A string implicitly includes a '\0' null terminator at the end.
This detail is critical for arrays and string manipulation later on.
Boolean Types
In C23, bool, true, and false have become core language keywords.
In older C standards, they were typically utilized via <stdbool.h>.
#include <stdbool.h>
bool ok = true;
In engineering, your syntax choice must align with your compilation standard.
If your project still uses C11/C17 as a baseline, #include <stdbool.h> is the safer route.
Using sizeof to Inspect Object Size
sizeof returns the number of bytes occupied by a type or object.
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof age);
The result type of sizeof is size_t.
To print it, use the %zu format specifier.
Do not use %d to print a size_t.
Format mismatches pose an undefined behavior risk.
Constants and const
The const qualifier indicates that an object cannot be modified through the current identifier.
const int max_count = 100;
const is not exclusively for "compile-time constants."
It acts more like a read-only constraint.
Pay special attention when const is combined with pointers.
const int* p; // The int pointed to by p cannot be modified via p
int* const q; // The pointer q itself cannot be modified
Reading complex declarations from right to left aids comprehension.
Storage Class Keywords
C includes a set of keywords that influence the linkage and storage duration of identifiers.
| Keyword | Common Meaning |
|---|---|
auto |
Automatic storage duration; default for local variables |
static |
Static storage duration or internal linkage |
extern |
Declares an external definition |
register |
Historical optimization hint; has very little modern relevance |
thread_local |
Thread-local storage; formalized in C23 |
static is the most easily misunderstood.
Inside a function, it preserves a local variable's state across invocations.
At file scope, it restricts the name's visibility exclusively to the current source file.
static int counter = 0;
Global static is a mechanism for encapsulation, not a modifier to be sprinkled arbitrarily.
Type Qualifier Keywords
| Keyword | Purpose |
|---|---|
const |
Read-only view |
volatile |
Special access semantics; NOT for thread synchronization |
restrict |
Promise of exclusive pointer access (aliasing hint) |
atomic |
Atomic access; covered in later concurrency chapters |
volatile is not a concurrency synchronization tool.
You must internalize this from the beginner phase.
Shared state across threads should be managed using atomics or locks.
Control Flow Keywords Overview
These keywords dictate the execution path of the program:
if else switch case default
for while do
break continue return goto
In the introductory phase, master if, switch, for, while, break, continue, and return.
goto will be explained later in the context of C's error cleanup paths; it is not a default control flow tool.
Type Construction Keywords Overview
struct union enum typedef
These are used to construct more complex data models.
struct aggregates multiple fields.
union allows multiple fields to share the exact same block of storage.
enum defines a set of named integer constants.
typedef creates an alias for an existing type.
These are not mere syntax features; they are foundational data layout tools.
Identifier Naming Rules
Variable names, function names, and type names are all identifiers. Basic rules:
- Composed of letters, numbers, and underscores.
- Cannot begin with a number.
- Cannot be a reserved keyword.
- Should not use names reserved for the implementation.
Names starting with an underscore—especially double underscores or an underscore followed by a capital letter—are generally reserved for the implementation (compiler and standard library). Do not use them in your project code.
Exercise Caution with Implicit Conversions
C performs numerous implicit conversions within expressions. Examples include integer promotion, mixed integer and floating-point arithmetic, and signed-to-unsigned conversions.
int a = 5;
double b = 2.0;
double c = a / b;
Here, a is implicitly converted to a double.
However, the following code behaves differently:
int x = 5;
int y = 2;
double z = x / y;
x / y performs integer division first, yielding 2, which is then converted to double (2.0).
These subtle details directly impact business logic accuracy.
Engineering Risks
Even basic types can manufacture severe defects:
- Uninitialized variables leading to unpredictable behavior.
- Signed overflow triggering undefined behavior.
- Unsigned wraparound destroying boundary checks.
- Formatted output type mismatches causing stack read errors.
- Misapplying
sizeofon pointers instead of arrays, causing allocation size errors. - Mistaking
volatilefor a thread synchronization primitive.
These are not advanced topics; they are red flags you must be vigilant about from phase one.
Summary
In C, a variable assigns a name to object storage. Types dictate how that object is interpreted. Keywords govern declarations, control flow, storage duration, and access rules. Basic syntax may appear simple, but every construct maps directly to memory sizes, conversion protocols, linking boundaries, and resource safety. By adopting this perspective from day one, subsequent topics like pointers and compiler behaviors will not feel like sudden leaps in difficulty.