Оператор присваивания. Запись значения в переменную на Си
Пора разобраться, как использовать переменные по их прямому назначению — для хранения данных.
Основной вопрос такой: "Как сохранить значение в переменной?", или, вспоминая аналогию с коробкой, "Как положить что-то в коробку?"
Ответ прост. Для этого существует специальный оператор, который называется оператором присваивания и обозначается символом =
. Обычно говорят не сохранить значение в переменную, а присвоить переменной значение.
Как работает оператор присваивания?
Сразу будем разбираться на примерах.
Листинг 1. Присваивание значения переменной
int z; // объявляем переменную целого типа с именем z
z = 5; // сохраняем в переменную число 5
double pi = 3.1415926; // создаём переменную и сразу присваиваем ей значение
В примере выше показаны два способа присвоить значение переменной. В первом случае мы сначала создаём переменную, а потом записываем в неё значение, а во втором — сразу создаём переменную и присваиваем ей значение. В умных книжках второй вариант записи ещё называют инициализация переменной. Так что, если где-то встретите выражение навроде проинициализируем вещественную переменную pi
значением 3.1415926
, то не пугайтесь.
Второй вариант записи (напоминаю, инициализация) короче, но делает всё то же самое, что и первый. Берёт значение, которое записано справа от знака равенства, и сохраняет его в переменную, которая записана слева от от него.
Частая ошибка!
Не забывайте ставить после очередной инструкции точку с запятой ;
. Без неё компилятор не поймёт, где заканчивается одна инструкция (команда) и начинается другая.
Есть несколько важных моментов на которых надо заострить внимание:
в некоторых языках программирования, после того как вы создали переменную, в ней сразу же хранится значение нуль. В языке Си это не так. Пока переменной не присвоено какое-нибудь значение, в ней будет храниться "мусор". Как этот мусор выглядит, мы посмотрим на следующем уроке.
справа от знака равенства не обязательно должно быть записано какое-то конкретное число, там может быть записано, например, арифметическое выражение или имя другой переменной, объявленной ранее.
Остановимся на втором пункте подробнее. Чтобы лучше разобраться давайте обратимся к примерам.
Если справа от знака равенства записано выражение, то сначала вычисляется его значение и уже оно сохраняется в переменную, которая записана слева от =
.
Листинг 2.
int n;
n = 1 + 2; // В переменную n записываем значение 3
Важно!
Если переменной присваивается значение, то предыдущее значение, которое в ней было, удаляется.
Листинг 3.
int n = 10; // Создаём переменную и записываем в неё 10
n = 5; // Теперь в переменной записано значение 5, значение 10 удалено.
Обратите внимание, новое значение не добавляется к старому, а полностью заменяет его. Помните я говорил, что переменные всё-таки не совсем коробки? Что коробки это только наглядный образ, но полной аналогии нет. Теперь мы видим, что наша аналогия с коробкой не совсем точная. Если бы в коробке лежало десять шариков, и мы бы туда положили ещё пять, то их стало бы 15.
В выражении справа от знака равенства могут использоваться другие переменные, которые объявлены ранее. Вместо них в выражение подставляются значения, которые в них хранятся в данный момент.
Листинг 4.
int n = 10, x = 0; // можно инициализировать сразу несколько переменных
n = 5; // теперь n равно 5
x = n - 3; // x будет равно 2 (5-3)
При вычислении выражения n - 3
, n
равняется 5
. Это потому, что исходное значение 10
, заданное при инициализации, мы в программе изменяли. А при вычислениях используется текущее значение переменной.
В выражении справа от знака равенства может быть использована сама переменная.
Листинг 5.
int n = 10;
n = n + 5; // n будет равно 15 (10 + 5)
Кажется, что это какой-то порочный круг, но на самом деле проблем здесь не возникнет. Т.к. сначала вычисляется значение выражения справа, и только потом происходит присваивание. По аналогии с предыдущим примером при вычислении значения справа от знака равенства вместо переменной n
будет подставлено её текущее значение, т.е. 10
. Потом будет произведено сложение и присваивание.
Ещё один поучительный пример.
Листинг 6.
int x = 5, y = 0, z;
y = x + z; // что будет записано в переменной y?
Если вы подумали что 5
, то вы ошиблись. Неизвестно, что в неё будет записано т.к. неизвестно, что было записано в переменную z
. Ещё раз напоминаю: если переменной не присвоено никакое значение, в ней хранится не нуль, а неизвестно что.
Выражение справа от знака равенства может быть достаточно сложным, оно не обязательно будет состоять из одного действия.
Листинг 7.
int a = 2, b = 9, c = 4, D;
D = b*b - 4*a*c; // D будет равно 49 (9*9 - 4*2*4)
Почему именно так? У каждого арифметического действия есть свой "вес" , или, иначе говоря, приоритет. Например, вы уже наверняка знаете, что сначала в выражениях выполняется деление и умножение, а только потом вычитание и сложение. В программировании всё так же. Подробнее о приоритетах различных операций ещё будем говорить в следующих уроках.
Теперь ещё один важный момент, который надо понимать. Когда я рассказывал о типах данных, я говорил, что в переменной можно хранить значения только того же типа, что и тип переменной. Например, в переменную целого типа нельзя положить вещественное число. Этот принцип применяется и с выражениями. Чтобы программа работала корректно, необходимо, чтобы тип выражения или переменной справа от знака присваивания совпадал с типом переменной.
Посмотрим несколько примеров.
Листинг 8.
int n, a = 2;
double x, pi = 3.14;
char s = 'k'; // сохраняем в переменную s символ k.
// Сам символ нужно написать в одинарных кавычках.
s = pi; // неправильно. В коробку для символов пытаемся положить вещественное число
n = a*pi; // неправильно. В коробку для целых чисел пытаемся положить 6.28 (2*3.14)
a = x; // неправильно. В целочисленную переменную пытаемся сохранить вещественное число
x = 2 + a; // ДОПУСТИМО!!! x будет равно 4.0
Внимание на последнюю строчку! Хотя справа выражение целого типа, а слева вещественная переменная, данная команда всё равно будет работать так, как надо. Это из-за того, что все целые числа можно записать как вещественные, у которых дробная часть равна нулю. Подобное действие называется неявное приведение (преобразование) типов.
С этой возможностью надо быть очень осторожным, т.к. компилятор не отслеживает такие ошибки. Другими словами, программа будет скомпилирована, но работать будет неправильно. Мы ещё вернёмся к этому в следующем уроке, когда научимся выводить значение переменных на экран компьютера.
Практика
Исследовательские задачи для хакеров
- В некоторые языках программирования, для вывода значения переменной на экран, её можно просто передать в качестве аргумента в ту же функцию, что и для вывода строки. Например,
# python
print("Hello, World!");
n = 10
print(n)
// javascript
console.log("Hello, world!\n");
let n = 10;
console.log(n)
// php
echo "Hello, world";
$n = 10;
echo $n;
В языке Си так сделать не получится. Попробуйте скомпилировать и запустить следующую программу.
#include <stdio.h>
int main(void){
int n = 10;
printf(n); // Ошибка
}
Для того, чтобы вывести целочисленную переменную на консоль, нужно чуть-чуть изменить аргументы функции printf
. Вот так:
printf("%d", n);
Что это означает и как это работает, мы разберём уже в следующем уроке, а пока можете самостоятельно поэкспериментировать с выводом целочисленных значений и переменных в консоль.
- Кстати, теперь, зная, как выводить на экран значение переменной, попробуйте вывести на экран переменную типа
int
, которую вы объявили, но значение ей ещё не присвоили.
И напоминаю про хакерскую задачу из позапрошлого шага про типы данных. Теперь у вас есть всё, чтобы её выполнить.