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
- The compiler reads each declaration and stores the variable name with its type.
- The type determines how much storage is needed. You can inspect that with
sizeof(type); the result is a byte count of typesize_t. - The initializer is converted to the variable’s type if needed. For example,
21.5fis afloatliteral, while21.5is adoubleliteral. - Expressions are evaluated using rules based on their operand types. Two
intoperands use integer division; adoubleoperand makes the calculation floating point. - When values are passed to
printf, the format string tellsprintfhow 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
intfor ordinary whole numbers such as counts, menu choices, and loop indexes. - Use
doublefor most decimal calculations unless you specifically needfloat. - Use
charfor one character, not for words or names. - Use
unsignedonly when the value is truly never negative and you understand how unsigned arithmetic behaves. - Match every
printfconversion specifier to the value’s type:%dforint,%uforunsigned int,%ffordoubleinprintf,%cforchar, and%zuforsizeofresults. - 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
- Create variables for a product quantity, unit price, and letter grade. Print each value with a label and the correct format specifier.
- Write a program that stores
5completed lessons out of8total lessons and prints the completion ratio as0.62or0.63, depending on your chosen rounding. - Declare a
charcontaining'a', create anothercharfor 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.
intstores whole numbers, whilefloatanddoublestore floating-point numbers.doubleis usually the best default for decimal calculations.charstores 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
sizeofwhen exact storage size matters. - Correct format specifiers are essential when printing typed values with
printf.
