В программировании существует понятие "Переменная". Я очень долго не мог понять, что это такое. Возможно, какой-нибудь читатель попал на эту страницу, потому что испытывает сложности в осмыслении сущности "переменная".
Переменная — это нечто такое, что способно изменять свое значение в ходе выполнения программы
Вспомним самое обычные арифметическое уравнение: x = a + b.
В этом уравнении присутствуют три именованных неизвестных:
x
a
b
Каждое неизвестное в этом уравнении может изменять своё состояние: за названием неизвестного сокрыто какое-либо значение. В каких-то неизвестных значения задаём мы, какие-то неизвестные вычисляются по формулам с помощью ставшими изветными неизвестных. Любое изменение значения равносильно изменению состояния.
В компьютере почти все то же самое.
Переменная в языке C++ представляет собой название неизвестного, такого неизвестного, значение-состояние которому можно задавать или изменять во время работы программы
Информация в компьютере хранится в памяти. Минимальная единица информации хранится в одной ячейке памяти. У любой ячейки памяти есть свой собственный физический адрес. У каждой ячейки памяти свой индивидуальный физический адрес.
Когда мы хотим, чтобы компьютер, например, выводил некоторое значение на монитор, нам необходимо заставить компьютер запомнить это значение.
Запоминаемое компьютером значение попадает в некоторую область памяти, где и хранится.
Область памяти представляет собой целостный блок, собранный из какого-то числа ячеек.
Хранимое внутри области памяти значение определяется компилятором с помощью первой ячейки.
У каждой ячейки есть собственный физический адрес.
В C++ обращение к адресу первой ячейки некоторого блока памяти эквивалентно обращению ко всему блоку блоку памяти.
Чтобы хранимое в блоке памяти значение, например, отображалось на экране, нужно вытащить значение из блока памяти.
Чтобы вытащить значение из блока памяти, нужен адрес первой ячейки этого блока памяти.
Обращением к адресу первой ячейки блока памяти всегда можно вытащить значение, хранимое внутри всего блока.
C++
1
2
3
4
5
6
7
8
9
10
//Листинг 1
#include <iostream>
intmain(){
intx=100;
//Обе строчки выведут значение, хранимое в блоке памяти, названным x
std::cout<<(int)(*&x) << '\n';
std::cout<<x<<'\n';
}
Таким вот образом адрес некоторой отдельной ячейк памяти выступает как посредник для вписывания чего-то в память и вытаскивания чего-то из целого блока памяти.
Поименованный блок памяти, внутри которого разрешено проводить изменения, и является переменной.
Программисты именуют блоки памяти любым разрешённым стандартом языка названием (имеются некоторые ограничения на именование переменных) и, используя выбранное имя, они, программисты, либо записывают в блок памяти значение, либо вытаскивают из блока памяти значение. Т. е. программисты, используя компилятор, оперируют блоками памяти.
Вот простой пример кода с использованием переменной:
C++
1
2
3
4
5
6
7
8
9
10
//Листинг 2
#include <iostream.h>
intmain(){
intx=2;//Переменная x
inty=3;//Переменная y
x=x+y;//Изменили x
cout<<x<<"\n";//Теперь x стал 5
cin.get();
}
Если временно не обращать внимание на int, то можно заметить, что сам по себе пример кода напоминает школьный пример из алгебры. Здесь и x и y — это названия, за которыми сокрыты неизвестные данные: эти данные будут изменяться в ходе работы программы. Такие названия являются названиями блоков памяти.
Переменная есть блок памяти, разрешающий изменять хранимые внутри себя данные
.
Процесс работы программы, созданной с помощью листинга №2:
В начале работы программы в x присвоится 2, в y, соответственно, 3
После присваиваний значений в переменные срабатывает часть кода, изменяющая переменную x, согласно какому-то выражению (сейчас этим выражением является формула x+y)
Изменённое значение остаётся лежать в блоке памяти, названным x, поэтому это значение можно вытащить в любой момент времени, пока компилятор C++ уведомлен об этом блоке памяти
Так как в блоке памяти происходят изменения с данными, то такие именованные блоки памяти называют переменными (перемена значений).
Т. е. в одном блоке памяти в разные моменты времени могут лежать разные данные.
Не во всех блоках памяти программисты позволяют компиляторам вносить изменения. Изначально, при именовании блоков памяти, компилятор выделяет блоки памяти, изменять данные внутри которых разрешено свободно, но иногда нужно иметь возможность сделать блок памяти только для чтения. Такие блоки памяти являются константами. Но речь сейчас о переменных, поэтому углубляться не будем.
Компиляторам C++ нужно сообщать, каких размеров должны быть блоки выделяемой памяти. Такого вида информирование происходит путём объявления типов. Объявление типа переменной не что иное, как объяснение компилятору необходимых размеров для блока памяти.
Типы переменным в C++ задаются только один раз, изменять их в ходе работы программы нельзя, т. е. нельзя изменять размеры выделенных блоков памяти, можно только уничтожать один блок ради создания другого (это при работе с указателями).
В C++ типы делятся на фундаментальные (они же и базовые, и примитивные) и составные.
К фундаментальным типам относятся:
Символьный вид типов: char, unsigned char, signed char — относится к целочисленным типам
Целочисленные виды типов: short int, int, long int, long long int — относится к целочисленным типам
Расширенный символьный вид типов: wchar_t — относится к целочисленным типам,
Булев вид типов: bool — относится к целочисленным типам (3.9.1 — 7)
Вид типов с плавающей точкой (в народе — дробные числа): float, double, long double — относится к типам с плавающей точкой
Вид пустого типа: void — относится к неполным типам. (3.9.1 — 9)
Указатели на нестатические члены классов (эти указатели отличаются по поведению от обычных указателей)
Для работы с числами используют целочисленные типы, эти типы могут быть знаковыми и беззнаковыми. Знаковые типы — это типы для переменных, способных принимать отрицательные значения, а беззнаковые, соответственно, отрицательных значений принимать не способны.
Поскольку числа имеют особенность быть отрицательными, то численным типам необходим знак + или —. Иногда не нужны отрицательные числа, тогда имеет смысл использовать беззнаковый тип, благодаря которому возможно использовать дополнительные положительные числа.
short int — Это тип. Указывается перед переменной, хранящей целые числа.
int — Это тип. Указывается перед переменной, хранящей целые числа.
long int — Это тип. Указывается перед переменной, хранящей целые числа.
В short int и long int слово int можно опускать: short, long
C++
1
2
3
shortx=10;// то же, что short int = 10
longy=10;// то же, что long int = 10
longlongz=10;//то же, что long long int = 10
Когда нужно использовать свойство знаковости чисел (только положительные, либо не только положительные, но обязательно и отрицательные), используют ключевые слова signed (знаковое) и unsigned (беззнаковое):
C++
1
2
3
4
5
6
7
8
unsignedx=-1;//x - беззнаковая переменная, поэтому отрицательное значение
// внутри неё быть не может
signedy=-1;//y - знаковая переменная, поэтому внутри неё законно может находиться отрицательное значение
//Выведите на экран значения x и y самостоятельно
unsignedshortz1=1;
unsignedintz2=1;//==>unsigned z1=1
unsignedlongz3=1;
В работе со знаками (при выборе объявления знаковых, либо беззнаковых типов) программист выбирает диапазон. Есть два диапазона чисел, эти диапазоны чисел имеют одинаковую ёмкость: в каждый можно уместить одинаковое количество чисел (если в первый 256, то и во второй 256, если в первый 1024, то и во второй 1024).
Например, некоторый тип способен принимать 6 числовых значений, тогда, в случае знаковости, программист всегда стоит перед выбором только одного из двух диапазонов.
Возраст человека не может быть отрицательным. Если бы тип некоторой переменной ограничивался умением воспринимать только 6 значений, то тогда знаковый тип (signed) не мог бы использовать значения 3, 4 и 5:
-5
-4
-3
-2
-1
0
1
2
3
4
5
6
-5
-4
-3
-2
-1
0
1
2
3
4
5
6
Согласитесь, что это не очень хорошо. Поэтому в некоторых изучаемых примерах вы будете встречать объявления беззнаковых типов (unsigned) либо явно знаковых (signed).
Имейте в виду, что несмотря на то, что при использовании беззнаковых типов появляется возможность использовать дополнительные положительные числа, вместимость самого типа никак не изменяется
Все целочисленные типы могут иметь одинаковый размер. То есть блокам памяти может выделяться одно и то же число байт, независимо от того, что в один блок памяти вносится, например, char, а в другой long long int.
Из целочисленных числовых типов для работы программ в большинстве случаев хватает типа int.
Размеры целочисленных типов во всех компиляторах C++ подчиняются следующему неравенству:
char <= short <= int <= long <= long long
Расчитать количество вмещаемых значений в знаковый и беззнаковый тип можно по формулам:
Левый (знаковый) -2n-1 … 2n-1 — 1
Правый (беззнаковый) 0 … 2n — 1
n — количество бит. Один байт = 8 бит.
Мир не ограничивается только целыми числами, поэтому существуют типы, называемые типами с плавающей точкой. В отличие от целочисленых типов они не могут быть объявлены как беззнаковые (либо знаковые):
C++
1
2
3
4
5
6
7
8
9
10
//компилятор clang
#include <iostream> //Если у вас старые компиляторы, то #include <iostream.h>
intmain(){
unsignedfloatx=1;//ошибка компиляции, числа с плавающей точкой не могут быть беззнаковыми
signedfloaty=1;//ошибка компиляции, числа с плавающей точкой не могут быть объвлены знаковыми
floatz=1;//Это работает
unsigneddoublea=1;//ошибка компиляции, числа с плавающей точкой не могут быть беззнаковыми
}
Размер выделяемой памяти для типов с плавающей точкой определяется реализацией компилятора. У чисел с плавающей точкой после точки какое-то определённое число значащих цифр, за значащами цифрами идут цифры, которые представляют собой информационный мусор, поэтому толку от них никакого нет.
В Borland C++ 3.1 для float выделяется 4 байта и даётся 7 значащих цифр после запятой, для double выделяется 8 байтов и даётся 15 значащих цифр после запятой.
C++
1
2
3
4
5
6
7
8
//В Borland C++3.1
#include <iostream>
intmain(){
floatx=0.123456789;
cout<<x;//округляется в 0.1234567
cin.get();
}
C++
1
2
3
4
5
6
7
8
9
10
11
// В компиляторах посвежее возможно такое проявление.
#include <iostream> // std::cout, std::fixed
#include <iomanip> // std::setprecision
usingnamespacestd;
intmain(){
floatf=3.123456789;//после 7 цифры за точкой цифры потерялись. Они не значащие
cout<<fixed<<setprecision(20)<<f<<'\n';
cin.get();
}
В совокупности целочисленные типы и типы сплавающей точкой называются арифметическими типами.
Примеры Объявления переменной в программе:
C++
1
2
3
4
5
inti;//Объявили переменную вне функции
intmain()
{
Кодпрограммы
}
C++
1
2
3
4
5
intmain()
{
inti;//Объявили переменную внутри функции
Кодпрограммы
}
При объявлении переменной можно указывать её значение
C++
1
2
3
4
5
6
inti=100;// целочисленная Переменная i равна значению 100
intmain()
{
Кодпрограммы
}
Имена переменным задают либо буквой, либо словом, состощим из букв латинского алфавита. Например, можно написать "int itog" – (целочисленная переменная "itog")
Если необходимо несколько переменных одного типа, то при объявлении оных можно разделять их запятыми:
C++
1
2
3
4
5
6
inta,b;//две независимые переменные одного типа
intmain()
{
Кодпрограммы
}
Когда указываем переменные, в окончании обязательно ставим точку с запятой
C++
1
2
3
4
5
6
7
inti;// Целочисленная переменная
chara;// Символьная переменная
floatx;// Дробная переменная (число с точкой)
intmain()
{
Кодпрограммы
}
Для закрепления материала можно выполнить простое упражнение:
1. Объявите две переменные и присвойте им значения.
2. Поменяйте значения, хранимые в переменных.
3. Попробуйте поменять местами переменные одинакового и различного типов, посмотрите, как ведет себя компилятор в различных ситуациях.
Например, вы объявили a=5; b=10. Необходимо сделать так, чтобы при выводе значения b на монитор выводилось число 5, а при выводе значения a число 10.
Вся эта тема весьма важна. Без понимания переменной углубление в программирование подобно рыбалке голыми руками. О переменных можно ещё рассказывать, но пока что нужно понять хотя бы то, что я пытался до вас донести с помощью этой своей статьи.
Отвлечение от основной темы.
Помню, как еще в самом начале долго не мог понять, что же такое эти странные переменные. Скорее всего я не первый и не последий испытал такую трудность.
Чтобы программа могла что-то сделать, программе нужен доступ к какому-либо участку памяти компьютера. Например, чтобы выполнить простой расчет суммы из двух слагаемых, программе нужно место под хранение первого слагаемого, второго слагаемого и место под хранение полученного результата. Т. е. для вычисления суммы может требоваться либо два, либо три участка памяти (если используется два, то результат записывается поверх использованного слагаемого). Результат вычисления часто необходим для дальнейшего вычисления, в том числе для повторяемых вычислений. Вот так выглядит простейшая простая программа вычисления суммы:
C++
1
cout<<5+10<<"\n";//Вывести на экран 15
Подход написания кодов подобным образом ведёт к тому, что нет возможности сохранять результаты для дальнейшего их использования. Вот получили мы в результате вычисления число 15, а как этот результат использовать дальше? Числа 5 и 10 в этом примере являются литеральными константами: они где-то сохранены в памяти ПК, иначе бы вычисление не представлялось возможным, но повторно обратиться к 5 и 10 мы уже не можем, потому что непосредственно с этими литеральными константами способа связи нет. Только считать заново.
Кроме этого, во время работы подобной программы вводить значения для вычислений с клавиатуры нельзя.
Поэтому, чтобы программы были полноценными, программисты почти всегда используют переменные. Редкая программа может оказаться полезной, если переменных в ней вобще нет.
Но сложно утверждать, что в таком коде много смысла, потому что используется значение, заданное в исходном коде программе, значение это непосредственно во время работы программы не изменить никак, с тем же успехом можно было использовать, например, литеральную константу, поэтому этот пример мало отличается от предыдущего.
Тем не менее, этот пример ближе к полноценному коду, ведь если требуется ввод с клавиатуры, то использование имени переменной позволяет связаться с участком памяти, в который компилятор сможет записывать изменения:
C++
1
2
3
4
5
6
7
8
9
#include <iostream.h>
intmain(){
intyear_birthay=0;
cout<<"Введи год рождения: ";
cin>>year_birthay;//Записали в память значение.
cout<<"Результат работы программы: "<<year_birthay+10<<"\n";//Использовали записанное значение
cin.get();
return0;
}
Благодаря возможности именования участков памяти мы можем написать почти любое название, которое будет удобно использовать в дальнейшем.
При именовании переменных нужно иметь в виду следующее:
Первым символом имени переменной не может быть цифра
Имя переменной может состоять только из символов латинского алфавита, цифр и символа подчёркивания
Символы в верхнем и нижнем регистрах рассматриваются как разные
В качестве имени нельзя использовать ключевые слова, зарезервированные компилятором
Не стоит начинать название с одного или двух символов подчёркивания: подобные имена используются разработчиками для нужд компилятора, поэтому использования подобных имён способно привести к неожиданным последствиям
Стандарт не ограничивает длину задаваемого переменной имени, но некоторые платформы могут вводит свои ограничения на длину
Тип переменной определяет, что именно в ней будет храниться. Хранимым значением может быть число, строка, какой-то наш собсвенный тип данных. Все типы, даже одинаковые, но с разным названием, воспринимаются компиляторами C++ как типы с разной структурой, т. е. считаются разными, поэтому у разных типов переменных могут различаться операции, которые можно использовать.
Так, например, если переменная имеет целочисленный тип, то для нее определена операция сложения +
Если переменная имеет тип "массив символов", то операции сложения + в таком типе нет. Если попробовать сложить символьные строки char* (не путайте со string), используя +, то ничего из этого не получится.
C++
1
2
3
4
5
6
intmain(){
char*p1="Hello";
char*p2="World";
char*p=p1+p2;//ошибка
}
Каждый тип может имеет свои особенности, поэтому при создании переменных вы должны правильно выбирать соответствующий им тип.
В C++ Имеется важное отличие между понятиями определения и объявления переменной.
Под объявлением понимается, собственно, задание имени переменной и указание ей нужного типа.
Под определением понимается внесение какой-то информации в объявленную переменную.
Размеры переменных зависят от вашей операционной системы. Чтобы посмотреть размер переменной, для нас существует оператор sizeof. Этот оператор имеет некоторую особенность, на которую в дальнейшем натыкаются многие новички:
Операция sizeof, применяемая к указателю отдаёт размер указателя, а не размер того, на что указатель указывает
Об указателях рано рассказывать, но имейте в виду, что у sizeof имеется особенность, связанная с указателями
C++
1
2
cout<<sizeof(int)<<"\n";//Узнали размер переменной типа int
cout<<sizeof(double)<<"\n";//Узнали размер переменной типа double
Иногда хочется узнать, какое максимальное и минимальное значение можно обрабатывать в переменной арифметического типа. В действительности эти значения зависят от платформы, в стандартной библиотеке C++ максимумы и минимумы реализованы в шаблоне numeric_limits. Числовые пределы заменяют и дополняют обычные препроцессорные константы языка С. Впрочем, эти константы по-прежнему доступны для целочисленных типов в заголовочный файлах <climits> и <limits.h>, а для вещественных типов — в заголовочный файлах <float> и <float.h>
В компиляторе Borland C++ 3.1 можно найти файлы с описанными лимитами в папке, в которую установлен борланд. (У меня, например, эти файлы находятся в C:\Borland\BORLANDC\INCLUDE)
В компиляторе mingw, с большой вероятностью, в папке, в которую установлен mingw (У меня, например, S:\Program Files\CodeBlocks\MinGW\include
В других компиляторах можно попробовать найти по аналогии
Если вы смогли найти файлы лимитов, то при желании посмотреть вовнутрь них, открывайте эти файлы нормальными текстовыми редакторами (AkelPad, Notepad++ и т. п.). После открытия можно увидеть нечто похожее на:
C++
1
#define INT_MAX 2147483647
Такая строчка обозначает, что компилятор может использовать константу INT_MAX, значение которой равно 2147483647.
C++
1
2
3
4
5
6
7
8
#include <limits.h> //Файл, в котором определены константы-максимумы.
intmain(){
cout<<INT_MAX<<endl;//Выводим максимальное значение для типа int на экран
cout<<INT_MAX+1;// А что будет если прибавить еще единичку
cin.get();
return0;
}
В показанном примере к числу, которое для типа int по определению является последним, прибавляется единица. Из-за этого происходит переполнение, и, если компилятор не настроен, чтобы не компилировать код в подобной ситуации, то скорее всего выдаст предупреждение о переполнении и выполнит код, в результате работы которого в переменной окажется наименьшее значение: это как пройти по кругу. Такое переполнение опасно.
Просто код:
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
#include <limits.h>
intmain(){
cout<<"Предельное значение int = "<<INT_MAX<<"\n";
cout<<"Предельное значение unsigned int = "<<UINT_MAX<<"\n";//U у UINT обозначет Unsigned
cout<<"Разность = "<<INT_MAX-UINT_MAX<<"\n";//Все, что было в минусовой части ушло к плюсовой и теперь это можно использовать
cin.get();
}
Все новички, начинающие использовать знаковость типов очень уязвимы к сложноуловимым ошибкам: знаковые типы должны работать совместно со знаковыми, а беззнаковые совместно с беззнаковыми, в случае смешивания знаковости требуется особое внимание к некоторым нюансам:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Пример кода, который из-за смешения работает не так, как
//можно было ожидать
#include <iostream>
usingnamespacestd;
intmain(){
unsignedx=UINT_MAX;//Получаем максимальное вмещаемое значение
unsignedtemp=x-1;//Запоминаем значение, меньшее максимального на единичку
/*Что-то делаем*/
for(inti=0;i<x;i++){
if(x==temp)cout<<"OK";//x никогда не сможет принять значение temp
//это очень сложно заметить
}
}
При смешивании знаковости будьте очень внимательны к обрабатываемым диапазонам значений
Объявлять явно знаковые и беззнаковые переменные можно двумя способами. Либо явно писать ключевые слова signed (unsigned), либо с помощью специального суффикса "u"
C++
1
2
3
4
5
6
signedcharch1=75;//Знаковая переменная ch1, тип у которой char
unsignedcharch2=258;//Беззнаковая переменная ch2, тип у которой char
signedintx1=100;//Знаковая переменная x1, тип у которой int
inty1=100u;//беззнаковая переменная y1, тип у которой int
unsignedy2=100;//беззнаковая переменная y2, тип у которой int (то же, что и для y1)
Просто код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
intmain(){
shortx1;//Знаковый. Короткий диапазон целых чисел
signedshortx2;//Знаковый. Короткий диапазон целых чисел
unsignedshortx3;//Беззнаковый. Короткий диапазон целых чисел
inty1;//Знаковый Диапазон целых чисел
signedinty2;//Знаковый Диапазон целых чисел
unsignedinty3;//Безнаковый Диапазон целых чисел
longz1;//Знаковый длинный дипазон целых чисел
signedlongz2;//Знаковый длинный дипазон целых чисел
unsignedlongz3;//Безнаковый длинный дипазон целых чисел
charch1;//или Знаковый или беззнаковый диапазон для кодов символов. Может быть в обоих вариантах.
signedcharch2;//Знаковый диапазон для кодов символов
unsignedcharch3;//Безнаковый диапазон для кодов символов
//Иногда в кодах символов применяют отрицательные значения, а иногда только положительные.
// short char; // неверно. char не может быть коротким или длинным.
// signed double d; //неверно. Знаковыми и беззнаковыми могут быть только целочисленные типы
Спасибо за уроки, насколько я понял, объект cin.get() в конце программы должен дождаться нажатия ENTER, для окончания работы программы, но у меня для просмотра вывода работы программы её нужно продублировать, не могли бы Вы объяснить, почему?
Такое происходит из-за оставления символа ‘\n’ в потоке ввода.
Происходит из-за смешения ввода: в поток ввода вводятся числа, потом туда запихивается символ. Нажатие Enter туда и влезает, поток съедает нажатие Enter. Т. е. Enter съелся = Enter нажат. То же, что дождался.
Спасибо за уроки, насколько я понял, объект cin.get() в конце программы должен дождаться нажатия ENTER, для окончания работы программы, но у меня для просмотра вывода работы программы её нужно продублировать, не могли бы Вы объяснить, почему?
Такое происходит из-за оставления символа ‘\n’ в потоке ввода.
Происходит из-за смешения ввода: в поток ввода вводятся числа, потом туда запихивается символ. Нажатие Enter туда и влезает, поток съедает нажатие Enter. Т. е. Enter съелся = Enter нажат. То же, что дождался.
Я не могу это хорошо объяснить. Как сумел. Вот некоторые решения такой проблемы:
http://www.cyberforum.ru/cpp-beginners/thread1260162.html#post6641410
Спасибо за помощь, разобрался.
Вместо cin.get(); cin.ignore(2);
2 — количество игнорируемых символов.
Благодарю! Очень доходчиво. Но сразу не запомнить (мне).
пошел на обучение, много что не понял у вас все так расписано идеально, жму крепко руку ! спасибо