C Data Types

A data type in C tells the compiler what kind of value a variable stores. It controls how many bytes are used, how the stored bits are interpreted, which operations are valid, and which printf format specifier should be used. Choosing the right type is one of the first ways you make a C program correct, efficient, and understandable.

Overview: How Data Types Work

C is a statically typed language, so every variable has a type known at compile time. When you write int count = 5;, the compiler records that count is an integer variable. Later, if you use count + 2, the compiler generates integer arithmetic. If you write double price = 19.99;, it generates floating-point arithmetic instead.

At the machine level, memory is just bytes. A byte pattern such as 01000001 has no built-in meaning by itself. Interpreted as a character on common systems, it represents 'A'. Interpreted as a small integer, it represents 65. The type gives those bits meaning.

The most common beginner types are int, float, double, and char. An int stores whole numbers. A float stores decimal numbers with limited precision. A double stores decimal numbers with more precision and is the usual choice for general decimal calculations. A char stores one character, but it is also a small integer type under the hood.

C also has type modifiers such as short, long, signed, and unsigned. These change the range or signedness of integer types. For example, unsigned int stores only non-negative values, usually allowing a larger positive maximum than int. Exact sizes and ranges can vary by platform, so serious code uses sizeof and standard headers such as <limits.h> when it needs precise limits.

Syntax

int age = 30;
unsigned int inventory = 125u;
float temperature = 21.5f;
double total = 49.95;
char grade = 'A';
Part Meaning
int A whole-number type, commonly used for counts, indexes, and ordinary integer values.
unsigned int A whole-number type that cannot represent negative values.
float A single-precision floating-point type. Use an f suffix for float literals.
double A double-precision floating-point type, preferred for most decimal calculations.
char A type for one character, written with single quotes such as 'A'.

Use single quotes for a character literal and double quotes for a string literal. 'A' is a char-style value; "A" is a string containing a character plus a null terminator. You will study strings later, but the distinction matters immediately.

Examples

Using Basic Types

#include <stdio.h>

int main(void)
{
    int students = 28;
    float temperature = 21.5f;
    double price = 14.99;
    char grade = 'A';

    printf("Students: %d\n", students);
    printf("Temperature: %.1f\n", temperature);
    printf("Price: %.2f\n", price);
    printf("Grade: %c\n", grade);

    return 0;
}

Output:

Students: 28
Temperature: 21.5
Price: 14.99
Grade: A

This program uses four common types and prints each one with a matching format specifier. %d prints an int, %f prints floating-point values passed to printf, and %c prints a character. The precision forms %.1f and %.2f control how many digits appear after the decimal point.

Choosing Integer and Floating-Point Types

#include <stdio.h>

int main(void)
{
    int items = 7;
    int boxes = 2;
    int boxes_needed = (items + boxes - 1) / boxes;
    double exact_boxes = (double) items / boxes;

    printf("Items: %d\n", items);
    printf("Boxes needed: %d\n", boxes_needed);
    printf("Exact division: %.2f\n", exact_boxes);

    return 0;
}

Output:

Items: 7
Boxes needed: 4
Exact division: 3.50

Integer arithmetic is useful when the answer should be a whole number. The expression for boxes_needed rounds up using integer division, so 7 items placed 2 per box require 4 boxes. The exact_boxes calculation casts items to double first, producing decimal division instead of integer division.

Characters Are Numeric Values Too

#include <stdio.h>

int main(void)
{
    char letter = 'C';
    char next = letter + 1;

    printf("Letter: %c\n", letter);
    printf("Code: %d\n", letter);
    printf("Next: %c\n", next);

    return 0;
}

Output:

Letter: C
Code: 67
Next: D

A char is printed as a character with %c and as a number with %d. On systems using ASCII-compatible character codes, 'C' has the numeric value 67, and adding 1 gives 'D'. This numeric nature is useful, but code should not rely on every possible character encoding unless the program controls its environment.

How It Works Step by Step

  1. The compiler reads each declaration and stores the variable name with its type.
  2. The type determines how much storage is needed. You can inspect that with sizeof(type); the result is a byte count of type size_t.
  3. The initializer is converted to the variable’s type if needed. For example, 21.5f is a float literal, while 21.5 is a double literal.
  4. Expressions are evaluated using rules based on their operand types. Two int operands use integer division; a double operand makes the calculation floating point.
  5. When values are passed to printf, the format string tells printf how to read and display the argument bytes. A mismatched specifier can produce incorrect output or undefined behavior.

The sizeof operator is especially important because C only guarantees relative facts for many types. For example, sizeof(char) is always 1, but an int is not guaranteed to be exactly 4 bytes by the language standard. On common modern systems it is, but portable code checks instead of assuming when size matters.

Common Mistakes

Expecting Integer Division to Keep Decimals

If both operands are integers, / performs integer division and discards the fractional part. Convert one operand before dividing when you need a decimal result.

#include <stdio.h>

int main(void)
{
    int completed = 7;
    int total = 10;
    double ratio = (double) completed / total;

    printf("Ratio: %.2f\n", ratio);
    return 0;
}

Output:

Ratio: 0.70

Using the Wrong Literal for a Character

A single character uses single quotes. A string uses double quotes. Store one character in char, and use %c to print it.

#include <stdio.h>

int main(void)
{
    char answer = 'Y';

    printf("Answer: %c\n", answer);
    return 0;
}

Output:

Answer: Y

Choosing float When double Is Better

float saves memory in large arrays and some graphics or embedded programs, but ordinary calculations usually use double. It gives more precision and is the default type for decimal literals.

#include <stdio.h>

int main(void)
{
    double subtotal = 19.95;
    double tax_rate = 0.08;
    double total = subtotal + subtotal * tax_rate;

    printf("Total: %.2f\n", total);
    return 0;
}

Output:

Total: 21.55

Best Practices

  • Use int for ordinary whole numbers such as counts, menu choices, and loop indexes.
  • Use double for most decimal calculations unless you specifically need float.
  • Use char for one character, not for words or names.
  • Use unsigned only when the value is truly never negative and you understand how unsigned arithmetic behaves.
  • Match every printf conversion specifier to the value’s type: %d for int, %u for unsigned int, %f for double in printf, %c for char, and %zu for sizeof results.
  • Remember that floating-point values are approximations. Do not expect every decimal fraction to be stored exactly.
  • Compile with warnings such as -Wall -Wextra; compilers often catch suspicious conversions and mismatched format strings.

Practice Exercises

  1. Create variables for a product quantity, unit price, and letter grade. Print each value with a label and the correct format specifier.
  2. Write a program that stores 5 completed lessons out of 8 total lessons and prints the completion ratio as 0.62 or 0.63, depending on your chosen rounding.
  3. Declare a char containing 'a', create another char for the next letter, and print both as characters and numbers.

Summary

  • A C data type tells the compiler how to store and interpret a value.
  • int stores whole numbers, while float and double store floating-point numbers.
  • double is usually the best default for decimal calculations.
  • char stores one character and also behaves like a small integer.
  • Integer division and floating-point division behave differently.
  • Type sizes can vary by platform, so use sizeof when exact storage size matters.
  • Correct format specifiers are essential when printing typed values with printf.