Variables and fundamental types
A variable is a named object with a type. The type fixes how many bytes the object occupies, which operations are valid on it, and how printf must be told to print it. C has a small set of fundamental types and builds everything else from them, so the first step is to know what each one stores.
Declaring and initialising
A declaration names the type and the variable; an initialiser gives it a value at the same moment. Reading an object that was never initialised is undefined behaviour, so this course initialises every variable where it is declared. _Bool stores 0 or 1 and is printed with %d.
#include <stdio.h>
int main(void)
{
int pages = 320;
double price = 24.5;
char grade = 'A';
_Bool in_stock = 1;
printf("pages: %d\n", pages);
printf("price: %.2f\n", price);
printf("grade: %c\n", grade);
printf("in stock: %d\n", in_stock);
return 0;
}
gcc -Wall -Wextra vars.c -o vars
./vars
pages: 320
price: 24.50
grade: A
in stock: 1
Each conversion in the format string has a matching type: %d for int, %.2f for double with two decimals, %c for a single character. GCC checks the pairing when the format is a literal, which the last section shows.
How big is a type
The standard fixes minimum ranges, not exact sizes. On x86_64 with the usual ABI an int is four bytes and a long is eight; on a 32-bit system a long is four. sizeof answers the question for the machine you are on, and its result has type size_t, printed with %zu.
#include <stdio.h>
int main(void)
{
printf("char %zu byte\n", sizeof(char));
printf("short %zu bytes\n", sizeof(short));
printf("int %zu bytes\n", sizeof(int));
printf("long %zu bytes\n", sizeof(long));
printf("long long %zu bytes\n", sizeof(long long));
printf("float %zu bytes\n", sizeof(float));
printf("double %zu bytes\n", sizeof(double));
return 0;
}
gcc -Wall -Wextra sizes.c -o sizes
./sizes
char 1 byte
short 2 bytes
int 4 bytes
long 8 bytes
long long 8 bytes
float 4 bytes
double 8 bytes
Never write these numbers into a program that must work elsewhere. Use sizeof, or the fixed-width types from stdint.h, which lesson 6 introduces.
Integer division
When both operands are integers, / truncates toward zero and % gives the remainder. Mixing an integer with a double converts the integer first, so a cast on one operand is enough to get a fractional result.
#include <stdio.h>
int main(void)
{
int apples = 7;
int people = 2;
printf("integer: %d\n", apples / people);
printf("double: %.1f\n", (double) apples / people);
printf("remainder: %d\n", apples % people);
return 0;
}
gcc -Wall -Wextra division.c -o division
./division
integer: 3
double: 3.5
remainder: 1
The cast applies to apples only; the division is then done in double precision and the result is 3.5. Without the cast the fraction is lost before it reaches printf.
Characters and strings
char stores one byte, usually a character code. A string is a different thing: an array of char ending with a zero byte, written as a literal in double quotes. sizeof on the array counts the terminator too.
#include <stdio.h>
int main(void)
{
char letter = 'C';
char word[] = "C course";
printf("letter: %c\n", letter);
printf("word: %s\n", word);
printf("word bytes: %zu\n", sizeof word);
return 0;
}
gcc -Wall -Wextra chars.c -o chars
./chars
letter: C
word: C course
word bytes: 9
Eight characters plus the terminating zero make nine bytes. Lesson 23 takes the terminator apart; for now the important part is that %s expects a pointer to such an array, while %c expects a single character value.
The compiler checks the format
The file below has two mistakes: a character constant with two characters in it, and a double printed with %d. GCC reports both without running the program, and the second message names the conversion that should have been used.
#include <stdio.h>
int main(void)
{
char broken = 'ab';
double ratio = 0.5;
printf("%d\n", broken);
printf("%d\n", ratio);
return 0;
}
gcc -Wall -Wextra badchars.c -o badchars
badchars.c: In function 'main':
badchars.c:5:19: warning: multi-character character constant [-Wmultichar]
5 | char broken = 'ab';
| ^~~~
badchars.c:5:19: warning: overflow in conversion from 'int' to 'char' changes value from '24930' to '98' [-Woverflow]
5 | char broken = 'ab';
| ^~~~
badchars.c:9:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
9 | printf("%d\n", ratio);
| ~^ ~~~~~
| | |
| | double
| int %f
A multi-character constant has an implementation-defined value; here the bytes of ab are packed into an int and then cut down to a char. The format mistake is worse: the program compiles and prints garbage, because printf reads the bits of a double as if they were an int.
Exercises
- Declare one variable of every type from the sizes program and print each value with the correct conversion.
- Compute the average of three integers in two ways, once with integer division and once with a cast, and explain the difference in output.
- Print the size of an array of ten int values and the size of one element, then divide to recover the count.
Questions
A new set of questions in a new order every time. Choose one answer for each, then check.
-
What type does
sizeofreturn?Answer
size_tsizeofproduces a value of typesize_t, which is why the examples print it with%zu. -
What does
7 / 2evaluate to when both operands areint?Answer
3
Integer division truncates toward zero, so the fractional part is discarded and the result is 3.
-
What does
(double) 7 / 2evaluate to?Answer
3.5
The cast converts 7 to double first, so the division is done in double precision and gives 3.5.
-
What does
7 % 2evaluate to?Answer
1
%gives the remainder of the integer division, and 7 divided by 2 leaves 1. -
What does
sizeof "C course"evaluate to?Answer
9
The literal has eight characters plus the terminating zero byte, so the array occupies nine bytes.
-
What does
sizeof(long)print on the x86_64 machine used here?Answer
8
On this ABI a
longis eight bytes, the same size as along longand a pointer. -
What happens when a program reads a local variable that was never initialised?
Answer
The behaviour is undefined
An uninitialised object holds an indeterminate value, and reading it is undefined behaviour; this course initialises every variable.
-
Which conversion prints a
doublewith exactly two digits after the decimal point?Answer
%.2f%.2fprints a floating-point value with a precision of two digits after the point. -
This program is built with
gcc -std=c17 -Wall -Wextra badchars.c -o badchars. What does GCC warn about?#include <stdio.h> int main(void) { char broken = 'ab'; printf("%d\n", broken); return 0; }Answer
multi-character character constant [-Wmultichar]
A character constant with more than one character has an implementation-defined value, and GCC warns with
-Wmultichar. -
This program is built with
gcc -std=c17 -Wall -Wextra badfmt.c -o badfmt. Which warning flag does GCC print?#include <stdio.h> int main(void) { double ratio = 0.5; printf("%d\n", ratio); return 0; }Answer
-Wformat=
The
%dagainst a double produces the format warning, whose flag is-Wformat=.
The code on this page was run with gcc (Alpine 15.2.0) 15.2.0 on Alpine Linux 3.24.3 (x86_64) in a Docker container.
Article author: Arthur Isaev