Phase 1 · C FoundationsModule 2~44 min read

Variables, Data Types & Operators

Store and transform data with C's fundamental types, constants, expressions, conversions, and operators.

What you'll learn

Every C object has a type, a region of storage, and a lifetime. This lesson turns those ideas into practical code: declarations, fundamental types, constants, expressions, conversions, and the rules that keep arithmetic well-defined.

By the end, you'll be able to:

  • Declare and initialize variables with appropriate types
  • Use arithmetic, comparison, logical, and assignment operators correctly
  • Predict common implicit conversions and apply explicit casts deliberately
  • Explain why type sizes and signed overflow require care

Variables & declarations

A variable is a named object whose type determines which values it can represent and which operations make sense. In C, declare a variable before using it. Initialize it immediately whenever you already know its first value.

variables.c
#include <stdio.h>

int main(void) {
    int age = 25;          // declaration + initialization
    double price = 19.95;
    char grade = 'A';

    age = 26;              // assignment: replace the stored value

    printf("Age: %d\n", age);
    printf("Price: %.2f\n", price);
    printf("Grade: %c\n", grade);
    return 0;
}

Uninitialized is not zero

An uninitialized automatic variable has an indeterminate value. Reading it can produce unpredictable results or undefined behavior. Prefer declarations such as int count = 0;.
A type gives stored bits a meaning
ageint

25

typically 4 byte(s)

pricedouble

19.95

typically 8 byte(s)

gradechar

'A'

exactly 1 byte(s)

readybool

true

implementation-defined byte(s)

Fundamental types

FamilyExamplesPurpose
char'A'Characters, small integers, and individual bytes
short, int, long, long long-42Signed whole numbers with increasing minimum ranges
unsigned ...42UNonnegative modular integers and bit manipulation
float, double, long double3.14Approximate real-number calculations
booltrueBoolean intent through stdbool.h
voidno valueNo return value or generic pointer base type
The standard guarantees relationships and minimum ranges—not one universal byte size for every type.

C represents false as zero and true as nonzero. Including <stdbool.h>provides the readable aliases bool, true, and false.

booleans.c
#include <stdbool.h>
#include <stdio.h>

int main(void) {
    bool enrolled = true;
    bool can_graduate = enrolled && (120 >= 120);

    printf("Can graduate: %s\n", can_graduate ? "yes" : "no");
    return 0;
}

Modifiers & fixed-width types

Modifiers let you request a different range or signedness. Exact sizes still depend on the implementation. When a binary format or protocol requires exactly 32 or 64 bits, use the optional fixed-width types from <stdint.h>.

Language standards matter

Digit separators such as 9'000 arrive in C23; they are invalid in C17. The corrected C17 version below uses suffixes that match the intended types.
invalid-c17.c
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>

int main(void) {
    unsigned int attempts = 3U;
    long long distance = 9'000; // not valid in C17: use 9000
    int32_t temperature = -12;
    uint64_t stars = UINT64_C(10000000000);

    printf("%u %lld %" PRId32 " %" PRIu64 "\n",
           attempts, distance, temperature, stars);
    return 0;
}
fixed_width.c
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>

int main(void) {
    unsigned int attempts = 3U;
    long long distance = 9000LL;
    int32_t temperature = -12;
    uint64_t stars = UINT64_C(10000000000);

    printf("%u %lld %" PRId32 " %" PRIu64 "\n",
           attempts, distance, temperature, stars);
    return 0;
}

Tip

The macros from <inttypes.h>, such as PRId32, provide portable printf formats for fixed-width integer types.

Constants, literals & sizeof

Use const for an object your program should not modify. Preprocessor constants remain useful for compile-time sizes and conditional compilation, though they have no C type. The sizeof operator reports storage in bytes as a size_tvalue, printed with %zu.

constants.c
#include <stdio.h>

#define MAX_STUDENTS 30

int main(void) {
    const double tax_rate = 0.11;
    int scores[MAX_STUDENTS] = {0};

    printf("int: %zu bytes\n", sizeof(int));
    printf("scores: %zu bytes\n", sizeof scores);
    printf("elements: %zu\n", sizeof scores / sizeof scores[0]);
    printf("tax: %.0f%%\n", tax_rate * 100.0);
    return 0;
}
Typical output on a platform where int is four bytes; use sizeof instead of assuming.

Note

One C byte is exactly sizeof(char), which is always 1. A byte is at least eight bits, but the standard does not require every machine to use eight-bit bytes.

Operators & expressions

An expression computes a value. Operators combine operands into larger expressions. Arithmetic on two integers produces an integer, so 17 / 5 is 3, not 3.4.

operators.c
#include <stdio.h>

int main(void) {
    int a = 17;
    int b = 5;

    printf("%d\n", a + b);  // 22
    printf("%d\n", a / b);  // 3: integer division
    printf("%d\n", a % b);  // 2: remainder

    a += 3;                   // same as a = a + 3
    printf("%d\n", a);
    printf("%d\n", a > b && b != 0);
    return 0;
}
CategoryOperators
Arithmetic+ - * / %
Comparison== != < <= > >=
Logical! && ||
Assignment= += -= *= /= %=
Increment++ --

Equality uses ==

A single = assigns. Two equals signs, ==, compare. Compilers often warn about accidental assignment in a condition—another reason to keep warnings enabled.

Conversions & casts

C automatically promotes smaller integer types and finds a common type for mixed operands. Assignment then converts the result to the destination type. An explicit cast should make a deliberate conversion visible, not silence a warning you do not understand.

conversions.c
#include <stdio.h>

int main(void) {
    int total = 7;
    int count = 2;

    double wrong = total / count;          // int division first: 3.0
    double average = (double) total / count; // 3.5
    int truncated = (int) 9.8;             // 9

    printf("%.1f %.1f %d\n", wrong, average, truncated);
    return 0;
}

Key idea

Cast before the operation whose behavior you need to change. Casting the result oftotal / count would be too late because integer division has already discarded the fraction.

Overflow & undefined behavior

Unsigned arithmetic is modular: it wraps within the type's range. Signed integer overflow is undefined behavior, meaning the C standard places no requirements on the result. Validate ranges before arithmetic when values may approach their limits.

overflow.c
#include <limits.h>
#include <stdio.h>

int main(void) {
    unsigned int counter = UINT_MAX;
    counter += 1U;                 // defined wraparound to 0
    printf("%u\n", counter);

    // Do not do this: signed overflow is undefined behavior.
    // int broken = INT_MAX + 1;
    return 0;
}

Watch out

Floating-point values are approximate, too. Never assume most decimal fractions are stored exactly, and avoid direct equality tests for results of substantial floating-point calculations.

Recap & quick check

Key takeaways

  • A C declaration gives an object a name and type; initialization supplies its first value.
  • Type widths vary, so use sizeof, limits headers, or fixed-width types when size matters.
  • Integer division discards the fractional part when both operands are integers.
  • Implicit conversions happen throughout expressions; cast only when the intended conversion is clear.
  • Unsigned arithmetic wraps, while signed integer overflow is undefined behavior.

Quick check

1. What value does 7 / 2 produce when both operands are int?

2. Which format specifier prints a size_t value?

3. What is true about signed integer overflow?

4. Why cast total before dividing by count?

You can now model and calculate with C values. Next up: Module 3 — Input, Output & Formatting, where your programs begin communicating with users.