A variable is a named location in memory. A type tells the compiler how much memory to allocate and how to interpret the bits stored there.
The basic types
int n = 123; /* integer: typically 4 bytes, range ±2 billion */
char c = 'A'; /* character: 1 byte, stores a small integer */
float x = 3.14f; /* floating-point: 4 bytes, approximate */
double d = 3.14159; /* double-precision float: 8 bytes */int is the type you will use most. char stores a single character
but is also a small integer — 'A' is the integer 65, the ASCII code
for the letter A. float and double store real numbers approximately;
they cannot represent most decimal values exactly.
Check the size of any type with 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));
return (0);
}%zu is the format specifier for size_t, the type sizeof returns.
Printf format specifiers
printf does not know what type you are passing — you tell it with
a format specifier in the string:
| Specifier | Type | Example |
|---|---|---|
%d | int | printf("%d", 123) |
%c | char | printf("%c", 'A') |
%f | float, double | printf("%f", 3.14) |
%.2f | float, double | printf("%.2f", 3.14) |
%s | string literal | printf("%s", "hi") |
%zu | size_t | printf("%zu", sizeof(int)) |
Passing the wrong type for a specifier is undefined behaviour. The
compiler will warn you with -Wall if it can catch it.
Arithmetic
int a = 10;
int b = 3;
printf("%d\n", a + b); /* 13 */
printf("%d\n", a - b); /* 7 */
printf("%d\n", a * b); /* 30 */
printf("%d\n", a / b); /* 3 — integer division truncates */
printf("%d\n", a % b); /* 1 — remainder */Integer division truncates toward zero: 10 / 3 is 3, not 3.333.
The remainder operator % gives the leftover: 10 % 3 is 1.
If you need the decimal result, use float or double — which one
depends on the precision you need. Precision is the number of
significant digits a value carries. float gives you roughly 6–7;
double gives you 15–16. For most programs double is the right
default: it is no slower on modern hardware and rounding errors are
less likely to accumulate over long calculations.
float a = 10.0f;
double b = 10.0;
printf("float: %.10f\n", a / 3.0f); /* 3.3333332539 — imprecise */
printf("double: %.10f\n", b / 3.0); /* 3.3333333333 — closer */The calculator skeleton
This is the data layer of calculator.c. Two numbers, four operations,
all results printed. No user input yet — that comes in the next page.
#include <stdio.h>
int main(void)
{
int a;
int b;
a = 10;
b = 3;
printf("%d + %d = %d\n", a, b, a + b); /* addition */
printf("%d - %d = %d\n", a, b, a - b); /* subtraction */
printf("%d * %d = %d\n", a, b, a * b); /* multiplication */
printf("%d / %d = %d\n", a, b, a / b); /* division */
printf("%d %% %d = %d\n", a, b, a % b); /* remainder */
return (0);
}%% in a format string prints a literal % — the % character is
the escape character for format specifiers, so it must be doubled to
print it.
Compile and run it. Change a and b. Observe what integer division
does with 7 / 2, 1 / 3, -7 / 2.
The next page adds control flow: reading the operator from the user and choosing which operation to perform.