Для форматированного ввода-вывода данных пользуются функциями scanf и printf. Первый аргумент функций содержит формат ввода-вывода. Далее следуют вводимые (выводимые) переменные. Следующая таблица представляет формат ввода-вывода элементарных типов данных в Си:
описание типа | тип | формат |
целочисленный, 4 байта | int | %d |
целочисленный, 8 байт | __int64 | %I64d |
целочисленный, 8 байт | long long | %lld |
действительный, 4 байта | float | %f |
действительный, 8 байт | double | %lf |
символьный, 1 байт | char | %c |
строка, массив символов | char[], строка | %s |
Оператор присваивания в языке Си имеет вид знака равенства ‘=’.
Операторы в языке Си разделяются знаком ‘;’.
Комментарии в языке Си выделяются символами /* … */.
Комментарии до конца строки следуют после символов //.
Пример 1.1. Инициализируем переменные i (целое), j (вещественное), c (символьное) соответственно значениями 4, 5,4, ‘A’ и выведем их на экран. В дальнейшем операции ввода-вывода будем комментировать, указывая вводимые и выводимые значения.
#include <stdio.h>
int i = 4;
double j = 5.4;
char c = 'A';
int main(void)
{
printf("%d %lf %c\n", i, j, c); // 4 5.400000 A
return 0;
}
Символьным переменным можно присваивать не только символы, но и значения от 0 до 255. В таком случае переменная будет принимать значение того символа, ASCII код которого ей присвоен. Значения символьных переменных можно выводить как символы (используя формат вывода %c) или как числа – ASCII коды символов (используя формат вывода %d).
Напоминание! Сокращение ASCII расшифровывается как American Standart Code for Information Interchange.
Пример 1.2. Присвоим символьной переменной с значение 65 и выведем ее, используя форматы %c и %d. Напомним, что ASCII код символа ‘A’ равен 65.
|
#include <stdio.h>
char c = 65;
int main(void)
{
printf("%c %d\n", c, c); // A 65
return 0;
}
Упражнение 1.3. Напишите программу, которая выведет на экран строку из четырех символов, ASCII коды которых соответственно равны 3, 4, 5 и 6.
Пример 1.4. Рассмотрим программу, которая вводит два целочисленных числа a и b, вычисляет их сумму в переменной res и выводит на печать пример в формате
«слагаемое + слагаемое = сумма»
В качестве второго аргумента функции scanf следует передавать адреса переменных. Адрес переменной x обозначается & x.
#include <stdio.h>
int a, b, res;
int main(void)
{
scanf("%d %d",&a,&b); // a = 3, b = 5
res = a + b;
printf("%d + %d = %d\n", a, b, res); // 3 + 5 = 8
return 0;
}
Операции ввода-вывода можно также выполнять при помощи потоковых бесформатных функций cin и cout библиотеки <iostream>. Но они работают значительно медленнее, чем prinf и scanf. Поэтому для выполнения операций ввода-вывода рекомендуется пользоваться библиотекой <stdio.h>.
Пример 1.5. Перепишем программу из примера 1.4. вычисления суммы двух чисел с использованием функций cin и cout:
#include <iostream>
using namespace std;
int a, b, res;
int main(void)
{
cin >> a >> b; // a = 3, b = 5
res = a + b;
cout << res << endl; // 8
return 0;
}
Упражнение 1.6. Напишите программу, которая по заданным двум действительным числам a и b находит и выводит значение выражения a 2 + b 2.
БИТЫ. БАЙТЫ. СЛОВА
Битом называется единица информации. В одну ячейку памяти размером 1 бит можно занести два значения: 0 или 1.
Байтом называется ячейка памяти, состоящая из 8 битов.
Словом называется ячейка памяти, состоящая из двух байт или 16 битов.
|
Двойным словом называется ячейка памяти, состоящая из четырех байт или 32 битов.
В языках программирования различают, как правило, знаковые и беззнаковые типы данных. Беззнаковые типы данных хранят только неотрицательные значения, знаковые могут содержать как положительные, так и отрицательные числа.
Беззнаковые типы данных. Пусть имеется два бита. В них можно занести одно из четырех значений:
содержимое 2 битов | значение |
Соответственно в трех битах можно хранить значения от 0 до 7:
содержимое 3 битов | значение |
В n битах можно хранить числа от 0 до 2 n – 1. Например, в беззнаковом байте (8 бит) можно записать любое число от 0 до 255, а в беззнаковом двойном слове – число от 0 до 232 – 1 = 4294967295.
При выводе десятичных значений переменных беззнаковых типов вместо %d (decimal) пользуются %u (unsigned). Значения можно выводить также в беззнаковом восьмеричном типе %o (octal) и шестнадцатиричном %x (hexadecimal).
Знаковые типы данных. Старший бит знакового типа данных отвечает за знак числа. Число является положительным, если бит равен 0 и отрицательным, если бит равен 1. Например, в трех битах можно хранить знаковые значения от -4 до 3:
содержимое 3 битов | значение |
-1 | |
-2 | |
-3 | |
-4 |
В n битах можно хранить знаковые числа от -2 n -1 до 2 n -1 – 1. Например, в байте можно записать любое число от -128 до 127, в двойном слове – число от -231 = -2147483648 до 231 – 1 = 2147483647.
|
Целочисленный тип int в языке Си четырехбайтовый, поэтому переменные типа int могут хранить значения в промежутке [-231; 231 – 1] = [-2147483648; 2147483647].
Целочисленный тип shortint в языке Си двухбайтовый, поэтому переменные типа shortint могут хранить значения в промежутке [-215; 215 – 1] = [-32768; 32767].
Целочисленные типы longint и long в языке Си четырехбайтовые и ведут себя как обыкновенный тип int.
Целочисленный тип long long в языке Си восьмибайтовый, поэтому переменные типа long long могут хранить значения в промежутке [-263; 263 – 1] = [-9223372036854775808; 9223372036854775807].
Упражнение 1.7. Указать интервал чисел, которые можно хранить в 2 байтах в случае знакового и беззнакового типа.
Пусть – двоичная запись числа n. Тогда для получения десятичной записи числа - n необходимо инвертировать все биты в десятичной записи числа n и прибавить 1. То есть двоичная запись числа - n будет иметь вид , где через обозначено инвертирование бита .
Пример 1.8. Пусть под знаковый целочисленный тип отведено 4 бита. Какое десятичное число будет обозначать двоичная запись 1101?
Поскольку на первом месте стоит 1, то число отрицательное. Инвертируем биты: 0010 и прибавляем 1, получим 00112 = 310. Таким образом, число 1101 обозначает -3.
Пример 1.9. Если к макимальному целочисленному значению прибавить 1, то получится наименьшее целочисленное значение. Аналогично если из наименьшего значения типа int вычесть 1, то получится наибольшее значение типа int.
#include <stdio.h>
int i = 2147483647;
int main(void)
{
printf("%d %d\n", i, i + 1); // 2147483647 -2147483648
i++;
printf("%d %d\n", i, i - 1); // -2147483648 2147483647
return 0;
}
Константы INT_MIN и INT_MAX, объявленные в библиотеке <limits.h>, соостветственно равны наименьшему и наибольшему значению, которое может принимать переменная типа int.
Пример 1.10. Выведем значения переменных INT_MIN и INT_MAX.
#include <stdio.h>
#include <limits.h>
int i = INT_MIN, j = INT_MAX;
int main(void)
{
printf("Min Value:%d \nMax Value: %d\n", i, j);
// Min Value: -2147483648
// Max Value: 2147483647
return 0;
}
Пример 1.11. Выведем наибольшее и наименьшее значение восьмибайтового целочисленного знакового типа long long. Для того чтобы число имело тип long long, а не int, после него следует писать суффикс LL. Например, значение (1LL << 63) – 1 будет иметь тип long long и равняться 9223372036854775807 (19 десятичных знаков), а (1 << 63) – 1 будет типа int и равняться -1 (значение 1 << 63 при вычислении на 32 битах равно нулю).
#include <stdio.h>
long long i;
int main(void)
{
i = (1LL << 63) - 1;
printf("%lld %lld\n",i,i+1);
// i = 9223372036854775807 = 2 ^ 63 - 1
// i + 1 = -9223372036854775808 = -2 ^ 63
return 0;
}
Пример 1.12. Выведем наибольшее значение беззнаковых 32 и 64 – битовых целочисленных типов.
#include <stdio.h>
unsigned int i = -1;
unsigned long long j = -1;
int main(void)
{
printf("%u %llu\n",i,j);
// 4294967295 = 2 ^ 32 - 1, 18446744073709551615 = 2 ^ 64 - 1
return 0;
}
Пример 1.13. Выведем восьмеричное, десятичное, и шестнадцатиричное представление числа 255. Шестнадцатиричное представление чисел можно выводить как прописными (%x), так и заглавными (%X) буквами.
#include <stdio.h>
int i = 255;
int main(void)
{
printf("%o %d %x %X\n",i,i,i,i);
// 377 255 ff FF
return 0;
}
Унарный оператор sizeof () возвращает размер памяти в байтах, соответствующих типу или идентификатору.
Пример 1.14. Выведем размеры стандартных типов данных (в байтах).
#include <stdio.h>
int main(void)
{
printf("Size of char: %d\n",sizeof(char)); // 1
printf("Size of short int: %d\n",sizeof(short int)); // 2
printf("Size of int: %d\n",sizeof(int)); // 4
printf("Size of long int: %d\n",sizeof(long int)); // 4
printf("Size of long: %d\n",sizeof(long)); // 4
printf("Size of long long: %d\n",sizeof(long long)); // 8
printf("Size of double: %d\n",sizeof(double)); // 8
return 0;
}
Пример 1.15. Выведем размеры типов переменных (в байтах).
#include <stdio.h>
short int i;
long int j;
int main(void)
{
printf("Size of short int: %d\n",sizeof(i)); // 2
printf("Size of long int: %d\n",sizeof(j)); // 4
return 0;
}
ОПЕРАТОР ПРИСВАИВАНИЯ
Как мы уже увидели в разделе 2.5, оператор присваивания в Си имеет вид знака равенства “=”. Язык Си позволяет производить транзитивные присваивания. Например, оператор
a = b = 1;
сначала присвоит переменной b значение 1, а потом переменной a присвоит значение переменной b.
Пример 1.16. Целочисленные переменные a и b содержат некоторые значения. Поменять местами значения переменных a и b.
Решение. Задачу легко можно решить, используя третью целочисленную переменную t. Последовательность операций, ведущих к решению задачи, имеет вид:
t = a; a = b; b = t;
Упражнение 1.17. Решите задачу из примера 1.16, не вводя дополнительных переменных.
АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Основными арифметическими операциями являются: сложение (‘+’), вычитание (‘-‘), умножение (‘*’) и деление (‘/’). Порядок выполнения операций в выражении соответствует их приоритету. Операции с одинаковым приоритетом в выражении выполняются слева направо.
Операция деления (‘/’) выполняется согласно типу ее операндов. Если оба операнда являются целыми числами, то деление будет целочисленным. Если один из операндов является вещественным, то и результат будет вещественным. Например, пусть переменная x имеет целочисленный тип, а y действительный тип. Следующая таблица демонстрирует результаты деления для различных операндов:
Операция | результат |
x = 7 / 3; | x = 2 |
y = 7 / 3; | y = 2.000000 |
y = 7.0 / 3; | y = 2.333333 |
y = (double)7 / 3; | y = 2.333333 |
Рассмотрим второй пример. При выполнении операции присваивания значения выражения переменной, сначала вычисляется значение выражения, а потом оно присваивается переменной. Поскольку операнды во втором примере являются целыми, то результатом деления 7 / 3 будет 2. Потом целочисленное значение 2 преобразовывается в действительное значение 2.000000 и присваивается действительной переменной y.
В четвертом примере перед выполнением операции деления происходит преобразование типа делимого из целого в вещественный. Поэтому деление будет производиться без потери точности.
Пример 1.18. Найти среднее арифметическое двух целых чисел a и b.
Результатом вычисления выражения (a + b) / 2 может быть действительное число. Поэтому деление должно выполняться с сохранением точности. А для этого один из операндов необходимо преобразовать в действительный тип. Например, результат можно вычислить так: res = (a + b) / 2.0. Программа имеет вид:
#include <stdio.h>
int a, b;
double res;
int main(void)
{
scanf("%d %d",&a,&b);
res = (a + b) / 2.0;
printf("%lf\n",res);
return 0;
}
Операция вычисления остатка в Си обозначается символом ‘%’. При этом остаток при делении отрицательного числа на положительное является отрицательным (хотя математически остаток при делении на число n должен лежать в промежутке от 0 до n – 1 включительно).
Операция | результат |
x = 6 % 3 | x = 0 |
x = 8 % 3 | x = 2 |
x = -6 % 3 | x = 0 |
x = -8 % 3 | x = -2 |
В языке Си при выполнении операций возможны синтаксические сокращения. Например, вместо i = i + 1 можно писать i ++. Если <op> – некоторая бинарная операция, то вместо i = i <op> a можно писать i <op>= a. Примеры сокращений приведены ниже в таблице:
операция | сокращение |
i = i + 1 | i ++ |
i = i – 1 | i -- |
i = i + a | i += a |
i = i % a | i %= a |
Пример 1.19. Временем будем называть пару h: m, где h обозначает количество часов, а m – количество минут. Известно, что в h 1: m 1 начался дождь, а в h 2: m 2 он закончился (0 £ h 1, h 2 £ 23, 0 £ m 1, m 2 £ 59). Необходимо вычислить, сколько времени (hres: mres) шел дождь. Известно, что дождь продолжался не более 24 часов.
Если время h 1: m 1 больше чем h 2: m 2, то дождь начался в один день, а закончился на следющий. Например, если h 1: m 1 = 23:50 и h 2: m 2 = 13:20, то дождь длился 13 часов и 30 минут.
Времени h: m соответствует h *60 + m минут, прошедших с полночи. Тогда можно утверждать, что дождь начался в time 1 = h 1 * 60 + m 1 минут, а закончился в time 2 = h 2 * 60 + m 2 минут. Разность между началом и концом дождя составляет timeRes = (time 2 – time 1 + 24 * 60) % (24 * 60) минут. Выделяем количество часов и минут из timeRes и выводим их на экран.
#include <stdio.h>
int h1, h2, m1, m2, time1, time2, timeRes, hres, mres;
int main(void)
{
h1 = 23; m1 = 50;
h2 = 13; m2 = 20;
time1 = h1 * 60 + m1; time2 = h2 * 60 + m2;
timeRes = (time2 - time1 + 24 * 60) % (24 * 60);
hres = timeRes / 60; mres = timeRes % 60;
printf("%d:%d\n",hres,mres);
return 0;
}
Упражнение 1.20. Имеются одинаковые коробки, каждая из которых вмещает m шаров. Сколько коробок требуется для упаковки n шаров?
Упражнение 1.21. Рассмотрим условие предыдущей задачи. Сколько коробок будут полностью заполнены, если всего имеется n шаров, а каждая коробка вмещает m шаров?
Упражнение 1.22. Пусть n – трехзначное число. Присвоить переменным a, b, c соответственно количество сотен, десятков и единиц числа n.
Операции округления
округление вверх | |
округление вниз |
Пусть a и b – целые числа. Тогда
= a / b
= =
Пример 1.23.
= = 9 / 3 = 3,
= = 10 / 3 = 3,
= = 11 / 3 = 3