Типы данных(предыдущий урок)----
(следующий урок)В языке С при объявлении переменных ОБЯЗАТЕЛЬНО необходимо указывать соответствующие этим переменным типы. Мы до сих пор ограничивались единственным, целочисленным типом -
int. Настало время намного расширить круг представлений о типах данных, используемых в языке С.
Итак, для того, чтобы указать тип данных, используем
спецификатор типа - одно или несколько ключевых слов, определяющие тип объявляемой переменной. В языке СИ имеется стандартный набор типов данных, используя который можно сконструировать новые (уникальные, пользовательские) типы данных. Об этом речь будет позже, пока что разберем стандартные типы.
Целые типы:
int, char, short, long. Плавающие типы:
float, double. 1.
Целые типы. Надо сказать, что целые типы могут использоваться помимо названных выше спецификаторов типа с ключевыми словами
signed и
unsigned, но это необязательно. Они всего лишь указывают, на то- считать нулевой бит (по сути - это будет самый первый символ в двоичном представлении значения) объявляемой переменной знаком числа (то есть, + или -) или первой цифрой самого числа, т.е., если указано ключевое слово
unsigned, то нулевой бит интерпретируется как часть числа. Значит, такой тип не подходит для отображения отрицательных чисел. Если вы хотите указать, что ваша переменная будет принимать только неотрицательные значения- смело пишите
unsigned. В противном случае нулевой бит интерпретируется как знаковый. По умолчанию, если ключевое слово
unsigned отсутствует, целая переменная считается знаковой. Желающих разобраться поподробнее со знаковыми и беззнаковыми типами, с удовольствием отсылаю к следующей статье, разделам 9 и 10, которые этой теме посвящены
https://club.shelek.ru/viewart.php?id=126 Если вы напишете только
signed или
unsigned и далее не поставите больше никакого ключевого слова (сразу будет идти идентификатор переменной), то такая переменная будет рассматриваться как переменная типа
int.
Например:
unsigned int n;
unsigned int b;
int c; (подразумевается signed int c );
unsigned d; (подразумевается unsigned int d );
signed f; (подразумевается signed int f ). Для определения данных целого типа используются различные ключевые слова, которые определяют диапазон значений и размер области памяти, выделяемой под переменные (табл.). Отметьте для себя, что одни и те же типы данных на разных машинных платформах могут занимать различный объем памяти.
Тип | Размер памяти в байтах | Диапазон значений |
char | 1 | от -128 до 127 |
int | | |
short | 2 | от -32768 до 32767 |
long | 4 | от -2 147 483 648 до 2 147 483 647 |
unsigned сhar | 1 | oт 0 до 255 |
unsigned int | | |
unsigned short | 2 | от 0 до 65535 |
unsigned long | 4 | от 0 до 4 294 967 295 |
Отметим, что модификатор - типа
char используется для представления символа или для объявления строковых литералов. Значением объекта типа
char является
код (размером 1 байт), соответствующий этому символу. Почему не написан диапазон значений для типа
int и
unsigned int? В языке СИ не определено представление в памяти и диапазон значений для этих типов.
Размер памяти для переменной с модификатором типа
signed int определяется длиной так называемого машинного слова, которое имеет различный размер на разных машинах. Так, на 16-ти разрядных машинах размер слова равен 2-м байтам, на 32-х разрядных машинах соответственно 4-м байтам, т.е. тип
int эквивалентен типам
short int, или
long int в зависимости от архитектуры используемой ПЭВМ. Да кроме того, размер в байтах зависит еще от операционной системы и даже от компилятора. Таким образом, одна и та же программа может правильно работать на одном компьютере и неправильно на другом.
(!!!) Единственное, что гарантировано языком, это то, что short <= int <= long.Понятно, что вся эта путаница большого энтузиазма не вызывает Можно только порадоваться тому, что нам несколько легче: уже имеются стандарты языка. А в момент создания С, когда не было вообще никакой стандартизации, в размерах была еще большая неразбериха. Чтобы избежать того факта, когда из-за несовпадения размеров типов данных программа может работать на одном компьютере и не работать на другом, была придуман специальный оператор, который может определить длины памяти занимаемой переменной:
sizeof().
Работает он во время компилирования, а не во время исполнения программы, так что компилятор просто подставляет вместо него соответствующее число- размер переменной в байтах на вашем компьютере, в вашей операционной системе и с вашим компилятором. Пользоваться ей просто: в скобках вы указываете имя переменной, чей размер в байтах хотели бы получить. Допустим, вы определили переменную
х типа
int. Определим ее размер в байтах и выведем его не экран:
int x;
cout<<sizeof(x); 2.
Данные плавающего типаПеременные, представляющие число с плавающей точкой, то есть, представляющие собой десятичную дробь. Величина со спецификатором типа
float занимает 4 байта. Величина типа
double занимает 8 байт в памяти. Есть еще тип
long double. Беда, правда, в том, что у него разные размеры на разных компиляторах, а в некоторых он вообще отсутствует.
Примеры:
float f, a, b;
double x,y;
3.
Преобразование типов при вычислении выраженийНу вот мы познакомились с типами данных. Теперь стоит рассмотреть, что же происходит, если в одной и той же операции участвуют переменные различных типов.
При выполнении математических операций производится автоматическое преобразование типов, чтобы привести операнды выражений к общему типу или чтобы расширить короткие величины до размера целых величин, используемых в машинных командах. Выполнение преобразования зависит от специфики операций и от типа операнда или операндов.
Рассмотрим общие преобразования двух операндов разного типа в одном арифметическом операторе: в этом случае меньший преобразуется к большему. Здесь под "меньшим" понимается тип с меньшей абсолютной величиной максимального допустимого значения (т.е.
long будет меньше, чем
float). "наибольший размер" типа данных определяется не количеством занимаемых байт, а верхней границей диапазона представимых значений. Т.о.
unsigned получается больше, чем
int, а
float - больше, чем
long.
Пример:
double ft,sd;
unsigned char ch;
unsigned long in;
int i;
....
sd=ft*(i+ch/in);
Описание примера: при выполнении оператора присваивания правила преобразования будут выполняться следующим образом. Операнд
ch преобразуется в конечном счете к типу
unsigned long. По этому же правилу
i преобразуется к
unsigned long и результат операции, заключенной в круглые скобки будет иметь тип
unsigned long. Затем он преобразуется к типу
double и результат всего выражения будет иметь тип
double.
Пример:
int t;
char f;
long z;
t=f+z;
Значение переменной
f преобразуется к типу
long, вычисляется
f+z, результат преобразуется к типу
int и затем присваивается переменной
t.
В операциях присваивания тип значения, которое присваивается, преобразуется к типу переменной, получающей это значение. Допускается преобразования целых и плавающих типов, хотя такое преобразование может вести к потере информации.
(Отметьте для себя этот момент!). Сейчас пойдет краткое описание преобразования типов. Тому, кто хочет глубже и подробнее разобраться с этим советуем посмотреть двоичное представление типов и чисел.
1.Преобразование целых типов со знаком.- Целое со знаком преобразуется к более короткому целому со знаком, с потерей информации: все разряды числа, которые находятся выше (или, соответственно - ниже) границы, определяющей максимальный размер переменной.
- Целое со знаком преобразуется к более длинному целому со знаком. Путем размножения знака. Что это такое? Все добавленные биты двоичного числа будут заняты тем же числом, которое находилось в знаковом бите: если число было положительным, то это будет, соответственно 0, если отрицательным - 1.
- Целое со знаком к целому без знака: первым шагом целое со знаком преобразуется к целому со знаком, соответствующему целевому значению - то есть, тому, к которому приводим - (если целевой тип данных крупнее). У получившегося значения знак не отбрасывается, а просто все биты считаются обычными, в том числе и знаковый.
- Преобразование целого со знаком к плавающему типу происходит без потери информации, за исключением случая преобразования типа long int или unsigned long int к типу float, когда точность часто может быть потеряна.
2. Преобразование целых типов без знака.- Целое без знака преобразуется к более короткому целому без знака или со знаком путем усечения.
- Целое без знака преобразуется к более длинному целому без знака или со знаком путем добавления нулей слева.
- Целое без знака преобразуется к целому со знаком того же размера. Если взять для примера, unsigned short и short - числа в диапазоне 32768-65535 превратятся в отрицательные.
- Целое без знака преобразуется к плавающему типу. Сначала оно преобразуется к значению типа signed long, которое затем преобразуется в плавающий тип.
3. Преобразования плавающих типов. - Величины типа float преобразуются к типу double без изменения значения.
- Величины double преобразуются к float c некоторой потерей точности, то есть, количества знаков после запятой. Если значение слишком велико для float, то происходит потеря значимости, о чем сообщается во время выполнения.
- При преобразовании величины с плавающей точкой к целым типам она сначала преобразуется к типу long (дробная часть плавающей величины при этом отбрасывается), а затем величина типа long преобразуется к требуемому целому типу. Если значение слишком велико для long, то результат преобразования не определен. Обычно это означает, что на усмотрение компилятора может получиться любой мусор. В реальной практике с такими преобразованиями обычно сталкиваться не приходится.
В этих случаях превращения типа в тип носят
неявный характер, то есть, выполняются компилятором и любые сообщения о том, что такой-то тип приводится к такому-то отсутствуют. Хотя некоторые компиляторы все же сообщают о неявных преобразованиях, приводящих к потере точности или усечению. Неявное приведение типов возникает при выполнении операций присваивания, если значение одного типа присваивается переменной другого типа.
Кроме того, в Си есть возможность
явного приведения значения одного типа к другому. Для этого существует
операция приведения типов, которая пишется так:
( имя-типа ) операнд, где
имя-типа задает тип, к которому должен быть преобразован операнд.
Пример:
int i=2;
long l=2;
double d;
float f;
d=(double)i * (double)l;
f=(float)d;
В данном примере величины
i,l,d будут явно преобразовываться к указанным в круглых скобках типам.
Задание. Напишите программу, которая выводила бы на экран размер в байтах всех стандартных типов данных на вашем компьютере.