4.3. Размер данных и диапазон значений. Модификаторы типа.
4.3. Размер данных и диапазон значений. Модификаторы типа.
Как мы уже знаем байт состоит из восьми битов. Поскольку количество этих самых битов небольшое, то и, естественно, что записать с их помощью много чисел не получится. Поэтому размер переменных увеличивают до нескольких байтов. При этом занимаемое ими в памяти место увеличивается. Теперь нам необходимо возвратится к уже изученным типам данных и узнать о них то, о чем я раньше умолчал. Сейчас я расскажу, сколько каждый тип данных занимает места в памяти и в каком диапазоне он может принимать значение. Но прежде чем это делать, нужно объяснить еще один момент. Из школьных уроков математики вы должны знать, что существую положительные и отрицательные числа. В языке С++ такие числа относятся к знаковым, то есть предполагается что перед числом стоит плюс или минус. Ну и поскольку есть знаковые, то можно предположить что есть и беззнаковые, То есть всегда положительные, почему так, поймете дальше. Такие числа имеют отдельное обозначение - unsigned. Это ключевое слово ставится при объявлении переменной перед типом. Знаковые переменные тоже имеют обозначение - signed, но поскольку переменная по умолчанию считается знаковой, то его можно не использовать. Пример:
Code
unsigned int i;
а запись:
Code
signed int i;
идентична:
Code
int i;
ПРЕДУПРЕЖДЕНИЕ!!! Применение unsigned к float или double превращает тип переменной в int.
Ключевые слова signed и unsigned называются модификаторы(или спецификаторы) типа. Кроме названных есть еще несколько спецификаторов. Например, ключевые слова short и long которые применяются по отношению к целочисленному типу и увеличивают или уменьшают диапазон значений ну и размер. Теперь я приведу небольшую таблицу в которой представлены типы даны, занимаемый ими размер и диапазон значений. Стоп. Лучше мы напишем для этого программу. Чтобы это сделать, нам необходимо узнать, как получить размер переменной определенного типа и как вычислить диапазон. С размером все просто. В языке С++ есть специальная функция, которая умеет это делать – sizeof(). Ну а диапазон нам придется определять вручную, с помощью цикла. Помните, как мы это делали для генератора случайных чисел. Здесь будет нечто подобное, но мы воспользуемся интересным свойством переменных. Допустим, переменная имеет диапазон от 0 до 255. И сейчас ей присвоено максимальное значение 255. Как вы думаете, что будет, если прибавить к нему единицу? Значение переменной изменится на 0. Переменная будто закольцована сама на себя, и за пределы допустимого значения никогда не выйдет. Так и будет изменяться по кругу, в ту или иную сторону. Закольцованность работает в обе стороны, то есть если переменная равна нолю и отнять единицу, то значение станет 255.
В общем вот код:
Code
#include <iostream>
using namespace std;
void main () { setlocale(0,"");
bool b; char c;
int i=0; short int si=0; long int li=0; unsigned int ui=0; unsigned short int usi=0; unsigned long int uli=0;
long int lmin=0; long int lmax=0; do { ++li; if(li>lmax) lmax=li; else { if(li<lmin) lmin=li; } } while(li!=0); cout<<"\t"<<"long int"<<"\t\t"<<sizeof(li)<<"\t"<<lmin<<" ... "<<lmax<<"\n";
unsigned int uimin=0; unsigned int uimax=0; do { ++ui; if(ui>uimax) uimax=ui; else { if(ui<uimin) uimin=ui; } } while(ui!=0); cout<<"\t"<<"unsigned int"<<"\t\t"<<sizeof(ui)<<"\t"<<uimin<<" ... "<<uimax<<"\n";
unsigned short int usimin=0; unsigned short int usimax=0; do { ++usi; if(usi>usimax) usimax=usi; else { if(usi<usimin) usimin=usi; } } while(usi!=0); cout<<"\t"<<"unsigned short int"<<"\t"<<sizeof(usi)<<"\t"<<usimin<<" ... "<<usimax<<"\n";
unsigned long int ulimin=0; unsigned long int ulimax=0; do { ++uli; if(uli>ulimax) ulimax=uli; else { if(uli<ulimin) ulimin=uli; } } while(uli!=0); cout<<"\t"<<"unsigned long int"<<"\t"<<sizeof(uli)<<"\t"<<ulimin<<" ... "<<ulimax<<"\n";
Главную работу здесь выполняет вот этот кусочек, который повторяется несколько раз с некоторыми изменениями, в основном касающимися типа данных:
Code
int min=0; int max=0; do { ++i; if(i>max) max=i; else { if(i<min) min=i; } } while(i!=0); cout<<"\t"<<"int"<<"\t\t\t"<<sizeof(i)<<"\t"<<min<<" ... "<<max<<"\n";
Работает он как и в пример с генератором случайных чисел. Цикл начинается с нуля и завершается когда будет пройден круг и переменная вновь станет равна нолю. Тип переменных, в которых хранится минимальное и максимальное значение, должно соответствовать проверяемому типу, иначе оно может туда не поместится. Ну а в последней строке происходит отображение результата, в ней же с помощью sizeof вычисляется размер переменной в байтах.
Вот такая табличка получится в результате работы нашей программы:
Считать диапазон float и других последующих типов я не стал. Программа и так работает несколько минут, а тронь я float, работала бы несколько дней. Дело в том, что при одинаковом размере с типом int, float имеет диапазон от -1.0Е+37 до 1.0Е+37.(Если кто не знает что это значит, объясняю. +37 означает что нужно передвинуть точку вправо (от единицы) на тридцать сем знаков, подставив ноли. В результате получится число в котором 38 цифр. (Как его назвать я даже не знаю. Знаю есть квинтиллион, вроде 18 нолей, так ведь и это очень мало) Куда тут int с его 4 миллиардами. Ладно. Как видно из получившейся у нас таблицы, некоторые типы данных при одинаковом размере отличаются диапазоном значений. В первую очередь это касается знаковых и беззнаковых. Сейчас я объясню, почему так происходит. Если число имеет определенный знак, не важно, плюс это или минус, то естественно, что этот знак должен где-то храниться. В переменной под знак резервируется крайний левый бит, также называемый старшим битом. Если он равен 0 то переменная положительная(знак + ), если 1 то отрицательная(знак -). Потеря всего одного бита уменьшает значение переменной в два раза. Она как бы делится пополам - одна половина для положительных, другая - для отрицательных чисел, что вы и можете видеть. Упомянув о спецификаторах типа не возможно не рассказать еще об одном – const. Объявленную с его помощью переменную невозможно изменить. Такую переменную называют константой, и используют для хранения определенных постоянных значений. При объявлении константы ей нужно сразу же присваивать значение. Пример:
Code
const int p = 3,14;
Если где-то далее в коде вы попытаетесь ее изменить, компилятор сразу же сообщит об ошибке. Если вы не присвоите константе значения сразу, а попытаетесь сделать это позже, опять же получите ошибку. Так делать нельзя:
Code
const int p; p = 3,14; // ошибка. Изменить константу нельзя.