Типы данных и спецификаторы ввода-вывода
Типы данных — это «тары» разного размера
В C у каждой переменной есть тип. Тип определяет, сколько памяти занимает значение и как оно хранится. В рамках курса мы работаем в основном со следующими типами:
char— самый маленький тип данных (1 байт), обычно хранит один символ;int— целое число (на нашем компиляторе 4 байта);long long— большое целое (8 байт);float/double— дробные числа,doubleточнее.
Аналогия. Типы — это тары разного размера: рюмка, стакан, ведро. Нельзя налить ведро воды в рюмку — оно перельётся (переполнение). Так и значение не помещается в слишком маленький тип.
| Тип | Типичный размер | Диапазон (примерно) |
|---|---|---|
char | 1 байт | −128 … 127 |
int | 4 байта | −2.1·10⁹ … 2.1·10⁹ |
long long | 8 байт | −9.2·10¹⁸ … 9.2·10¹⁸ |
unsigned int | 4 байта | 0 … 4.2·10⁹ |
float | 4 байта | ~7 знаков точности |
double | 8 байт | ~15 знаков точности |
unsigned означает «только неотрицательные»: положительный диапазон примерно вдвое больше.
Размер типа можно узнать точно:
#include <stdio.h>
int main(void)
{
printf("int: %zu байт\n", sizeof(int));
printf("long long: %zu байт\n", sizeof(long long));
printf("char: %zu байт\n", sizeof(char));
return 0;
}
Переменные: объявление и присваивание
int count; /* объявление: выделили «ячейку» */
count = 5; /* присваивание */
int width = 10; /* объявление с начальным значением */
double price = 199.90;
char letter = 'A'; /* символ — в ОДИНАРНЫХ кавычках */
Арифметика работает привычно: +, -, *, /, а % берёт остаток от деления целых.
int a = 17, b = 5;
printf("%d %d\n", a / b, a % b); /* 3 2 — целая часть и остаток */
Важно. Если оба числа целые, то
/выполняет целое деление. Чтобы получить дробный результат, нужно, чтобы хотя бы одно значение было дробным:17 / 5.0.
Спецификаторы: «переводчик» между числом и текстом
Компьютер хранит числа в двоичном виде, а мы читаем и печатаем текст. Спецификатор формата
(%…) сообщает функции, как перевести одно в другое.
Аналогия. Спецификатор — этикетка на банке: «кг», «литры», «штуки». Написали на муке «литры» — покупатель запутается. Так и
printfс неправильным%напечатает мусор.
Вывод: printf
| Тип | Спецификатор | Пример |
|---|---|---|
int | %d | printf("%d", 42); |
unsigned | %u | printf("%u", 42u); |
long long | %lld | printf("%lld", 42LL); |
float / double | %f | printf("%f", 3.14); |
char | %c | printf("%c", 'A'); |
| шестнадцатеричное | %x | printf("%x", 255); |
| знак процента | %% | printf("100%%"); |
В printf и float, и double печатаются через %f — float автоматически повышается
до double.
Ввод: scanf
| Тип | Спецификатор | Пример |
|---|---|---|
int | %d | scanf("%d", &x); |
unsigned | %u | scanf("%u", &u); |
long long | %lld | scanf("%lld", &ll); |
float | %f | scanf("%f", &f); |
double | %lf | scanf("%lf", &d); |
char | %c | scanf(" %c", &c); |
Здесь float и double различаются: для double обязательно %lf (буква l — «long»).
Это одна из самых частых ошибок новичков.
Главное отличие ввода: нужен адрес
scanf должна изменить переменную, поэтому ей передают адрес — через &:
int x;
scanf("%d", &x); /* &x — адрес переменной x */
Аналогия. Значение — жилец, адрес — номер квартиры. Чтобы курьер положил посылку именно к вам, нужен номер квартиры, а не фотография жильца.
&x— это «номер квартиры» переменной.
За один вызов можно прочитать несколько значений — они разделяются пробелами или переводами строк:
int a, b;
double c;
scanf("%d %d %lf", &a, &b, &c); /* ввод: 3 4 2.5 */
printf("%d %d %.2f\n", a, b, c);
Ввод символа и «застрявший» Enter
Спецификатор %c читает любой символ, включая пробел и перевод строки. Если перед этим
вводилось число, «застрявший» Enter может попасть в %c. Лечится пробелом перед %c:
int n;
char c;
scanf("%d", &n);
scanf(" %c", &c); /* пробел перед %c пропускает пробельные символы */
printf("%d %c\n", n, c);
Частые ошибки
| Ошибка | Что не так |
|---|---|
scanf("%d", x); | забыли & — чтение по мусорному адресу |
scanf("%f", &d); для double | нужен %lf |
%d для double в printf | получите «мусор» вместо числа |
17 / 5 вместо 17 / 5.0 | целое деление вместо дробного |
'A' и "A" перепутаны | символ — в одинарных кавычках, строка — в двойных |
%c после числа без пробела | в символ попадёт перевод строки |
Мини-шпаргалка
int i;
long long ll;
float f;
double d;
char c;
scanf("%d", &i);
scanf("%lld", &ll);
scanf("%f", &f);
scanf("%lf", &d); /* для double — только %lf */
scanf(" %c", &c); /* пробел перед %c пропускает Enter */
printf("i=%d ll=%lld\n", i, ll);
printf("f=%.2f d=%.2f\n", f, d);
printf("c=%c\n", c);
Что запомнить: спецификатор обязан соответствовать типу, а scanf нужен адрес
переменной.