C · Beginner

Variables and Types in C

9 min readUpdated September 24, 2026Every example verified

In short: A C variable is a named piece of memory with a fixed type, declared before use as in int count = 0;. The type decides how many bytes it occupies and how those bytes are read: int and long hold whole numbers, double holds decimals, char holds one character and bool (from <stdbool.h>) holds true or false. A variable declared without a value contains whatever was in memory before.

Names for memory

Every value a C program works with lives somewhere in memory, and a variable gives one of those places a name and a type. int count = 0; reserves enough bytes for an int, calls them count and stores 0 there. From then on count in an expression means the value stored in those bytes, and count = 5 overwrites them.

The type is not decoration. It tells the compiler how many bytes to reserve and how to interpret them: the same four bytes mean one thing as an int and something entirely different as a float. It also fixes what operations produce, which is why dividing two int values gives an int (see operators). A variable's type never changes; only its value does.

The types you will use most: int for whole numbers (32 bits on every mainstream desktop compiler, roughly minus 2.1 billion to plus 2.1 billion), long long when a count can exceed that, double for numbers with a fractional part, char for a single character, which C stores as a small integer holding the character's code, and bool for true and false, available through <stdbool.h> since C99. float is a smaller, less precise decimal type; prefer double. sizeof(type) reports the byte size on the machine that compiles the program; the standard fixes only minimums, so the table below gives the sizes gcc uses on 64-bit Linux, which runs the examples here.

A declaration without an initial value, such as int total;, reserves the bytes but does not clear them. Reading total before assigning it is undefined behaviour: you get whatever happened to be there, or something stranger. Initialise variables when you declare them unless the very next statement assigns them.

Names start with a letter or underscore and continue with letters, digits or underscores; they are case sensitive and cannot be keywords such as int or return. const in front of a declaration makes the variable read-only after initialisation: const double VAT = 0.2; documents that the value is fixed, and the compiler rejects any later assignment.

Printing and reading need a specifier that matches the type exactly. printf uses %d for int, %ld for long, %lld for long long, %f for double (and for float, which is promoted), %c for char and %zu for the size_t that sizeof returns. scanf uses the same letters except that a double needs %lf, and every argument must be the address of the variable, written with &. A mismatch is undefined behaviour that usually prints nonsense, and the compiler does not reliably reject it.

Syntax

 C · syntax
type name;                    /* declared; value is undefined until assigned */
type name = value;            /* declared and initialised */
type a = 1, b = 2;            /* two variables of the same type */
const type NAME = value;      /* read-only after this line */

name = new_value;             /* assignment: the type stays, the value changes */

A declaration can appear anywhere a statement can (since C99), not only at the top of a block. Declare a variable close to where it is first used.

Common types with gcc on 64-bit Linux

TypeBytesprintfscanfHolds
int4%d%dwhole numbers, about -2.1 billion to 2.1 billion
unsigned int4%u%u0 to about 4.3 billion
long8%ld%ldwhole numbers to about 9.2 quintillion here; only 4 bytes on Windows
long long8%lld%lldat least 64 bits on every platform
double8%f%lfdecimals, about 15 significant digits
float4%f%fdecimals, about 7 significant digits
char1%c%cone character, stored as a small integer
bool (stdbool.h)1%d-true (1) or false (0)
size_t8%zu%zusizes and counts; the type sizeof produces

Declaring, printing and measuring

One variable of each common type is declared with a value and printed with its matching specifier; the last line shows sizeof.

 C
#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    int shelves = 12;
    long books = 48250L;
    double avg_weight = 0.85;
    char section = 'H';
    bool open_today = true;

    printf("Shelves: %d\n", shelves);
    printf("Books: %ld\n", books);
    printf("Average weight: %.2f kg\n", avg_weight);
    printf("Section: %c\n", section);
    printf("Open today: %d\n", open_today);
    printf("sizeof(int) = %zu, sizeof(double) = %zu\n", sizeof(int), sizeof(double));
    return 0;
}

Output

Shelves: 12
Books: 48250
Average weight: 0.85 kg
Section: H
Open today: 1
sizeof(int) = 4, sizeof(double) = 8

Each variable is declared with its type and an initial value, then printed with the matching specifier. %.2f rounds the double to two decimal places for display without changing the stored value. The bool prints as 1 because true is the integer 1. sizeof yields a size_t, printed with %zu; the sizes are gcc's on the 64-bit Linux build machine.

Reading three different types

One line of input holds a count, a temperature and a unit letter: 6 21.5 C.

 C
#include <stdio.h>

int main(void)
{
    int readings;
    double celsius;
    char unit;

    if (scanf("%d %lf %c", &readings, &celsius, &unit) != 3) {
        printf("bad input\n");
        return 1;
    }
    double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
    printf("%d readings, latest %.1f%c\n", readings, celsius, unit);
    printf("That is %.1f F\n", fahrenheit);
    printf("Code of '%c' is %d\n", unit, unit);
    return 0;
}

Input given to the program: 6 21.5 C

Output

6 readings, latest 21.5C
That is 70.7 F
Code of 'C' is 67

%d, %lf and %c read an int, a double and a char in that order, each into the address given by &. The space before %c in the format tells scanf to skip the blank between 21.5 and the letter; without it the char would receive the space itself. Because a char is a small integer, printing unit with %d shows its character code, 67 for a capital C.

Values change, types do not

The same int variable is assigned three times; the third assignment hands it a double.

 C
#include <stdio.h>

int main(void)
{
    int stock = 40;
    printf("Start: %d\n", stock);
    stock = stock - 15;
    printf("After sale: %d\n", stock);
    stock = 7.9;          /* the fraction is thrown away */
    printf("Assigned 7.9: %d\n", stock);
    const int capacity = 100;
    printf("Capacity: %d\n", capacity);
    return 0;
}

Output

Start: 40
After sale: 25
Assigned 7.9: 7
Capacity: 100

stock is assigned three times and prints three different values, but it is always an int. Assigning 7.9 does not fail: C drops the fraction, so the variable holds 7. capacity is const, so a later capacity = 90; would be a compile-time error; that is what const is for, letting the compiler stop a change you did not mean to make.

Common mistakes

  • Reading a variable before assigning it

    Why it goes wrong: int total; reserves memory without clearing it. total += 5 then adds 5 to garbage, and the result differs between runs, machines and optimisation levels because the behaviour is undefined.

    Fix: Initialise at the declaration: int total = 0;. gcc -Wall usually warns with may be used uninitialized.

     C · fix
    int total = 0;
    total += 5;
  • A printf specifier that does not match the type

    Why it goes wrong: printf("%d", 2.5) does not convert the double; printf reads an int from where it expects one and prints a meaningless number. gcc -Wall warns (format '%d' expects argument of type 'int'), but the program still compiles.

    Fix: Use %f for double, %d for int, %ld for long, %c for char; check the table above when unsure.

     C · fix
    double price = 2.5;
    printf("%.2f\n", price);
  • Forgetting & in scanf, or using %f for a double

    Why it goes wrong: scanf needs the address of the variable so it can store into it; passing n instead of &n hands it a value as if it were an address and typically crashes. %f with a double's address stores only four of its eight bytes.

    Fix: Write & before every plain variable in scanf and use %lf for double.

     C · fix
    double d;
    if (scanf("%lf", &d) == 1) {
        printf("%f\n", d);
    }
  • Assuming int can hold any whole number

    Why it goes wrong: A 32-bit int stops at 2147483647. A product or sum of large values silently exceeds that, and signed overflow is undefined behaviour: the program may print a negative number or anything else.

    Fix: Use long long (at least 64 bits) for totals and products that can grow, and check the input limits of an exercise.

     C · fix
    long long total = 0;
    total += 3000000000LL;
    printf("%lld\n", total);

Choosing types for a sensor log

Suppose a program collects temperature readings from a greenhouse every minute. Each reading has a fractional part, so it is a double; the number of readings is a whole number that fits comfortably in an int; the running sum of thousands of readings is still a double; a flag recording whether the heater was on is a bool; and the zone letter that identifies the sensor is a char. Choosing the type for each piece of data up front is most of the design: once the types are right, the printf and scanf lines follow from the table, and -Wall catches many of the remaining mistakes.

 C · in practice
double reading;
int count = 0;
double sum = 0.0;
bool heater_on = false;
char zone = 'B';

Key points

  • Declare before use: type name; or type name = value;.
  • The type fixes the size and meaning of the bytes and never changes; the value can.
  • Uninitialised variables hold garbage; reading them is undefined behaviour.
  • int for counts, long long for big totals, double for decimals, char for one character, bool from <stdbool.h>.
  • printf specifiers: %d, %ld, %lld, %f, %c, %zu; scanf uses %lf for double and needs &.
  • const makes a variable read-only after initialisation.
  • sizeof reports bytes on the compiling machine; the standard fixes only minimum sizes.

Try it yourself

Read an integer number of crates and a decimal weight per crate from one line, then print the total weight with two decimal places in the form Total: 37.50 kg. The input is 5 7.5.

Your program
#include <stdio.h>

int main(void)
{
    int crates;
    /* declare a double for the weight, read both values, print the total */
    return 0;
}
Input the program receives: 5 7.5
Expected output: Total: 37.50 kg

Practise this

Exercises for this lesson are in the C practice set.

Open the C playground

Frequently asked questions

What is the difference between float and double in C?

Both hold numbers with a fractional part, but a double uses 8 bytes and keeps about 15 significant digits, while a float uses 4 bytes and keeps about 7. Arithmetic on literals such as 2.5 is done in double anyway, and printf promotes floats to double, so double is the natural default. Use float only when you must store very many values and memory matters.

Why does printf use %f for a double but scanf needs %lf?

printf receives its arguments by value, and C promotes any float argument to double in a variadic call, so %f always sees a double and one specifier covers both types. scanf receives addresses, and a pointer to float and a pointer to double point at different sizes of storage, so it must be told which: %f for a float and %lf for a double. Using %f with the address of a double writes only half of its bytes.

How many bytes is an int in C?

The standard requires only that int can hold at least -32767 to 32767; the actual size is chosen by the compiler and platform. gcc on 64-bit Linux, Windows and macOS all use 4 bytes (32 bits). long differs: 8 bytes on 64-bit Linux and macOS but 4 on Windows, which is why long long is the portable choice for values beyond 32 bits. sizeof(int) tells you the truth for your compiler.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with GCC 13 (C11) at build time by the publishing checks, and the output shown is what it printed. Running C inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C11) locally.