Code of the Day
BeginnerHello, C

Variables and primitive types

Learn how C stores data with int, char, float, and double — including sizes, ranges, and how to print values with printf format specifiers.

CBeginner12 min read
Recommended first
By the end of this lesson you will be able to:
  • Declare and initialise variables of the four main primitive types
  • Explain what signed and unsigned mean for integer types
  • Use the correct printf format specifier for each type
  • Check the size of a type with sizeof

C is a language. Every variable has a type that is fixed at declaration time, and the compiler uses that type information to decide how much memory to allocate and what operations are valid. Unlike Python, where you can assign an integer to a variable and then assign a string to the same variable, in C the type is locked from the moment of declaration.

The four primitive types

C has four fundamental numeric types you will use constantly:

TypeTypical sizeWhat it stores
int4 bytesWhole numbers (−2 billion to 2 billion)
char1 byteA single character (or a small integer, −128 to 127)
float4 bytesDecimal numbers (about 7 significant digits)
double8 bytesDecimal numbers (about 15 significant digits)

A byte is 8 bits. Four bytes of int means 32 bits, which can represent 2^32 different values — split between negative, zero, and positive gives roughly ±2.1 billion.

#include <stdio.h>

int main(void) {
    int age = 28;
    char grade = 'A';
    float temperature = 36.6f;
    double pi = 3.14159265358979;

    printf("Age: %d\n", age);
    printf("Grade: %c\n", grade);
    printf("Temperature: %.1f\n", temperature);
    printf("Pi: %.10f\n", pi);

    return 0;
}

Notice the f suffix on 36.6f — without it, the literal 36.6 is a double, and assigning it to a float can generate a warning. The suffix tells the compiler it is already a float.

printf format specifiers

printf does not know the types of its arguments at runtime — you tell it through format specifiers in the format string:

FormatType
%dint (decimal)
%cchar
%ffloat or double
%lfdouble (in scanf; in printf both %f and %lf work)
%sstring (char *)
%zusize_t (result of sizeof)

The %.1f in the example above means "print with 1 decimal place." %.10f means 10 decimal places. This is printf's format string mini-language — the full reference is in the printf man page (man 3 printf).

Checking sizes with sizeof

The C standard does not fix the exact size of most types — it specifies minimum sizes and relationships. In practice int is 4 bytes on all modern platforms, but the language-portable way to check is sizeof:

#include <stdio.h>

int main(void) {
    printf("int:    %zu bytes\n", sizeof(int));
    printf("char:   %zu bytes\n", sizeof(char));
    printf("float:  %zu bytes\n", sizeof(float));
    printf("double: %zu bytes\n", sizeof(double));
    printf("long:   %zu bytes\n", sizeof(long));
    return 0;
}

sizeof returns a value of type size_t, which is an unsigned integer. Use %zu to print it correctly. Running this on a modern 64-bit machine typically gives: int 4, char 1, float 4, double 8, long 8.

Signed and unsigned

By default, int and char are signed — they can hold negative numbers. Prefix with unsigned to restrict to non-negative values and double the positive range:

int signed_int = -42;            /* -2,147,483,648 to 2,147,483,647 */
unsigned int unsigned_int = 42;  /* 0 to 4,294,967,295 */

char signed_char = -5;           /* -128 to 127 */
unsigned char byte_value = 255;  /* 0 to 255 */

Mixing signed and unsigned causes subtle bugs. If you compare a signed int with an unsigned int and the signed int is negative, C converts the signed value to unsigned first — and a negative number becomes a very large positive number. Always use the same signedness when comparing values.

Variable declaration and initialisation

C allows declaring a variable without initialising it:

int x;      /* declared but not initialised */
int y = 10; /* declared and initialised */

An uninitialised variable contains whatever bytes happen to be in memory at that address — this is called an indeterminate value. Reading an uninitialised variable is undefined behaviour in C: the compiler is allowed to assume it never happens, and can generate surprising code when it does. Always initialise your variables.

Multiple declarations on one line. C allows int a = 1, b = 2, c = 3; — all declared as int. Avoid this for complex initialisations; it makes the code harder to read. One declaration per line is cleaner.

The long and short variants

For situations where you need a larger or smaller range than int, C provides:

  • short int (or just short): at least 16 bits, typically 2 bytes.
  • long int (or just long): at least 32 bits, typically 8 bytes on 64-bit systems.
  • long long int (or long long): at least 64 bits.

For portable code with exact widths, #include <stdint.h> and use int32_t, int64_t, uint8_t, etc. These are guaranteed to be exactly the right size.

Where to go next

You know how C stores numbers and characters. Next: arithmetic and expressions — operators, operator precedence, integer versus floating-point division, and the modulo operator.

Finished reading? Mark it complete to track your progress.

On this page