Типы данных Всякая программа, предназначенная для реализации на компьютере, представляет собой формализованное описание алгоритма решения той или иной задачи Действия, выполняемые программой в соответствии с этим алгоритмом, направлены на преобразование некоторых объектов, определяющих текущее состояние процесса решения задачи Такие внутренние объекты программы принято называть данными Для хранения всякого элемента данных исполняющая система выделяет необходимое пространство в оперативной памяти компьютера, размер которого называется длиной этого элемента Важнейшей характеристикой любого элемента данных является его тип Типом данных называется определение некоторой структуры данных, формы ее представления в памяти и набора возможных операций с ней Понятие типа включает в себя следующую информацию об элементе данных: допустимый набор значений, которые объект этого типа может принимать в процессе работы программы (совокупность всех указанных значений мы будем называть областью определения типа) состав операций, которые разрешено выполнять над объектами данного типа способ представления элемента данных рассматриваемого типа в памяти машины правила выполнения всякой операции из допустимого для этого типа набора операций Данные, обрабатываемые средствами языка Си, могут иметь различную структуру, формы представления в компьютере Имеются типы данных, для которых формы представления и операции над ними реализованы в самом процессоре. Такие типы данных в Си называются базовыми Другие типы данных, хотя и определяются в языке, но реализуются программно, через последовательность машинных команд или процедур И, наконец, программист имеет возможность сам сконструировать производный тип данных средствами языка Си, написать набор операций (процедур) для работы с ним. В языке Си++ имеется возможность даже включить этот тип в перечень базовых типов данных и определить для него стандартные операции Все данные записываются последовательности битов в компьютере в виде некоторой В большинстве компьютеров нельзя обратиться к конкретному биту. Можно записывать или читать только машинное слово Машинное слово - двоичное число определенной размерности, используемое в основной системе команд компьютера для обработки данных. Размерность слова зависит от вида компьютера Байт - машинное слово минимальной размерности, адресуемое в процессе обработки данных. Размерность байта - 8 бит - принята не только для представления данных в большинстве компьютеров, но и в качестве стандарта: для хранения данных на внешних носителях; для передачи данных по каналам связи; для представления текстовой информации. Каждый байт кодирует один символ текста; для определения размерности машинных слов. Размерность машинного слова выбирается кратной байту. Стандартное машинное слово - машинное слово, размерность которого совпадает с разрядностью процессора. Для компьютеров типа IBM/PC размерность слова - 16 бит (или 2 байта). Машинное слово двойной длины (двойное слово) - используется для увеличения диапазона представления целых чисел. Двойные слова обрабатываются либо некоторыми командами процессора, либо программно. Для компьютеров типа IBM/PC размерность двойного слова - 32 бита (или 4 байта) Описание данных в программе Те элементы данных, которые сохраняют неизменные значения на протяжении всего времени работы программы, принято называть константами Другие же объекты, являющиеся предметом изменения в ходе выполнения алгоритма, называют переменными Переменной в языке Си называется область памяти для хранения данных, имеющая имя и некоторую внутреннюю структуру (тип данных) Любая переменная, которая используется в программе, должна быть определена В процессе анализа определения выполняются следующие действия: переменной транслятором вводится имя переменной, которое используется для обращения к ней в программе задается тип данных (структура данных), к которому относится переменная определяется начальное значение переменной (инициализация) транслятором распределяется память в программе для размещения переменной определяются характеристики переменной, которыми она будет обладать в программе (область действия, время жизни) С точки зрения языка Си, всякая переменная отождествляется с ее именем, или идентификатором величина С позиции же компьютера, она рассматривается как изменяющееся во времени содержимое некоторой области оперативной памяти В Си все переменные до их использования должны быть описаны При определении переменной должны быть заданы тип данных, имя переменной и, возможно, начальное значение Описание задает тип, а затем идет список одной или более переменных этого типа Язык Си обеспечивает возможность представления и обработки данных следующих основных типов: целые числа различной длины со знаком и без символы, представимые в формате стандарта ASCII вещественные числа различной длины Ключевые слова unsigned, signed, short и long могут быть использованы как модификаторы основных типов данных при построении производных типов. Например, следующие описания рассматриваются компилятором как эквивалентные: short int ...... ..........short long int .......... .. ... long unsigned int ............. unsigned unsigned short int ....... unsigned short unsigned long int ........ unsigned long signed char .............. char signed int ............... int signed short ............. short signed long .............. long Рассмотрим некоторые примеры описания данных в программе int a, b, c; float alpha, beta; int lower, upper, step; char c, line[512]; Поскольку внутренние машинные представления данных одних и тех же типов для различных компьютеров могут быть различными, то возникают определенные трудности при необходимости обеспечить надлежащую мобильность программного обеспечения. Так, например, всякая переменная типа int на машинах IBM PC занимает два байта памяти, в то время как для ее представления на компьютерах VAX-11 требуется четыре байта Преодолеть возникающие при этом трудности можно, используя, где это необходимо, операцию определения количества памяти, требуемой для представления некоторой переменной или какоголибо типа. В общем случае эта операция выглядит следующим образом: sizeof(name) где name есть либо идентификатор переменной, либо имя типа данных. В следующем примере a = sizeof(int); переменная a принимает значение, равное количеству байт памяти, требуемых для представления любой величины типа int, на конкретном компьютере Переменным в описаниях можно задавать начальные значения (инициировать). Это можно сделать, вводя инициализирующие выражения непосредственно в инструкции описания переменных char backslash = '\\'; int i=0; long day = 60*24; int ndigit[10]= { 0,0,0,0,0,0,0,0,0,0}; int mas[] = { 1, 2, 3 }; В последнем примере длина массива определяется по числу инициализирующих значений. Если размер любого типа пропущен, то транслятор определяет его длину, считая инициирующие значения Для символьных массивов существует инициации без скобок и запятых специальный способ char pattern[] = "the"; Это сокращение более длинного, но эквивалентного описания char pattern[] = {'t','h','e','\0'}; В первом случае длина массива полагается равной количеству символов в строке плюс один, а во втором равна количеству элементов в списке инициализации. В данном конкретном случае размер равен 4 (3 символа плюс закрывающий \0) С другой стороны, описание char line[80] = "This is a string"; объявляет массив, состоящий из 80 элементов типа char, но лишь 16 из них отличны от нуль-символа 1. Целые числа без знака Числа без знака - это положительные числа Для кодировки используется все разряды машинного слова Ключевое слово unsigned используется как модификаторы основных типов данных unsigned int b; unsigned int a; Байт (unsigned char) 7 6 5 4 3 2 1 0 0 1 0 0 1 1 0 1 Значение в 10-ой системе счисления = 1 + 4 + 8 + 64 = 7 Значение в 8-ой системе счисления = 0115 (начинается с 0) Значение в 16-ой системе счисления = 0x4D (начинается с 0x) Максимальное значение: 1 1 1 1 1 1 1 1 = 255 = 0377 = 0xFF Минимальное значение: 0 0 0 0 0 0 0 0 = 0 Слово (unsigned int) 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 1 1 0 1 1 0 1 1 1 0 0 1 1 0 0 в 8-ой системе = 0x66714 в 16-ой системе = 0x6DCC Максимальное значение: 1111111111111111 = +65535 = 0177777 = 0xFFFF Минимальное значение: 0000000000000000 = 0 Двойное слово (unsigned long int) Максимальное значение: 11111111 11111111 11111111 11111111 = +4294967297=037777777777=0xFFFFFFFF Минимальное значение: 00000000 00000000 00000000 00000000 = 0 2. Целые со знаком В системе команд любого компьютера имеется команда сложения целых чисел без знака. Операция вычитания, а также операция сложения чисел со знаком могут быть реализованы с использованием этой операции, но при условии специального кодирования отрицательных чисел, суть которого состоит в следующем: старшая цифра числа представляет знак числа положительные числа представляются обычным образом отрицательные числа представляются в дополнительном коде Дополнительный код используется для представления целых отрицательных чисел. Это позволяет вместо операции вычитания использовать обычное сложение, что упрощает конструкцию процессора При создании арифмометра Паскаль столкнулся с определенной трудностью: изобретенный им механизм переноса десятков работал при вращении счетных колес только в одном направлении, а это не позволяло производить вычитание вращением колес в противоположную сторону. Паскаль заменил вычитание сложением с дополнением вычитаемого. Для 8-разрядной машины, работающей в десятичной системе, дополнением числа А будет число (100 000 000А), поэтому операция вычитания В-А может быть заменена сложением: B + (100 000 000 - A) = 100 000 000 + (B-A) Получившееся число будет больше искомой разности на 100 000 000, но единица в 9 разряде просто пропадает при переносе десятков из восьмого. Алгоритм перевода отрицательного числа в дополнительный код следующий: каждая цифра отрицательного числа заменяется на дополнение ее до n-1, где n - основание системы счисления, т.е. на цифру, которая в сумме с исходной дает n-1 к полученному числу добавляется 1 Такое представление чисел со знаком называется дополнительным кодом. При сложении чисел со знаком, представленных в дополнительном коде, результат получается также в дополнительном коде. При этом операция выполняется по правилам сложения целых без знака Пример в десятичной системе счисления: 512 - 386 = 126 Переведем отрицательное 386 в дополнительный код - 3 8 6 - отрицательное число 1 6 1 3 - дополнение каждой цифры до 9 1 6 1 4 - добавление 1 Знак учитывается как (+, -) - (0,1) . Теперь разность 512-386 можно заменить суммой 0512 + 1614 -----------2126 Поскольку разряд (2 в двоичной системе 10) вышел за пределы разрядной сетки – переполнение. Поэтому число 1 2 6 – положительное Ответ: 512 - 386 = 0126 = +126 Другой пример в десятичной системе счисления: 119 – 386 0119 + 1614 -------------(01) 1 7 3 3 - 386 в дополнительном коде Знаковый разряд отрицателен (1 в двоичной форме - 01), число отрицательно, следовательно результат представлен в дополнительном коде. Его надо перевести в обычный вид 733 + 2 6 6 - дополнение каждой цифры до 9 2 6 7 - добавление 1 Ответ: Учитывая знаковый разряд: 119 - 386 = -267 В двоичной системе счисления дополнение каждой цифры заменяется инвертированием двоичного разряда, а с учетом представления знака старшим разрядом получается простой способ получения представления отрицательного числа: взять абсолютное значение числа в двоичной системе инвертировать все разряды, включая знаковый добавить 1 Ту же самую последовательность операций нужно выполнить, чтобы получить из дополнительного кода абсолютное значение отрицательного числа. Примеры: 0 000 0000 0010 0101 - прямой код числа +35 1 111 1111 1101 1010 - инверсия всех бит (обратный код) + 0 000 0000 0000 0001 - добавляем единицу мл.разряда ----------------------------------------1 111 1111 1101 1011 - дополнительный код числа (-35) F F D B - FFDB h - 16-ричная запись 0000 0110 1101 1000 - 0x06d8 - прямой код отрицательного числа 1111 1001 0010 0111 - 0xf927 - обратный код 1111 1001 0010 1000 F 9 2 дополнительный код 8 - 0xF928 - 16-ричная запись Целые со знаком могут быть Байтовые Целые Целые двойной длины (char - 1 байт) (int - 2 байта) (long int - 4 байта) char Максимальное значение: 01111111 = +127 Минимальное значение: 10000000 = -128 int Максимальное значение: 0111111111111111 = +32767 Минимальное значение: 1000000000000000 = -32768 long Максимальное значение: 01111111111111111111111111111111 = +2147483647 Минимальное значение: 10000000000000000000000000000000 = -2147483648 0 1 +32767 -1 -2 -16 -32767 0 1 0x7FFF 0xFFFF 0xFFFE 0xFFF0 0x8001 0000 0000 0111 1111 1111 1111 1000 0000 0000 1111 1111 1111 1111 0000 0000 0000 1111 1111 1111 1111 0000 0000 0001 1111 1111 1110 0000 0001 2. Символы, представимые в формате стандарта ASCII Символы представляются числом размером 1 байт (8 бит). В одном байте можно записать число от 0 до 255 ASCII (American Standard Code for Information Interchange — американский стандартный код для обмена информацией. ASCII представляет собой 7-битную кодировку для представления десятичных цифр, латинского и национального алфавитов, знаков препинания и управляющих символов. На подавляющем большинстве современных компьютеров минимально адресуемая единица памяти — 8-битный байт, поэтому там используются 8-битные, а не 7-битные символы. Обычно символ ASCII расширяют до 8 бит, просто добавляя нулевой бит в качестве старшего. Таблица кодов ASCII В компьютерах обычно используют 8-битные расширения ASCII История русских кодировок — это пример неразберихи, редкостной даже для нашей компьютерной действительности. Советские стандартизирующие организации принимали ГОСТы, производители компьютеров (Apple) и операционных систем (Microsoft) их дружно игнорировали и вводили собственные кодировки. В результате мы получили наследство из четырех разных ГОСТов, две кодировки от Microsoft (для DOS и для Windows) и кодировку от Apple для Mac'ов (все, естественно, несовместимые между собой). К счастью, сегодня нет нужды подробно описывать все эти кодировки, поскольку в Рунете выжили только две из них. Первая — это КОИ8-Р (КОИ означает Код для Обмена и обработки Информации, Р отличает русскую кодовую таблицу от украинской, КОИ8-У). КОИ8-Р была зарегистрирована Андреем Черновым из Релкома в качестве RFC 1489 и имеет вид: КОИ8-Р является стандартом de facto для всех служб Интернета, кроме WWW. В частности, все службы электронной почты и новостей Рунета работают в этой кодировке. Что касается Веба, то здесь ситуация сложнее. Дело в том, что более 90% клиентских компьютеров Сети работает под управлением Windows разных версий. Windows использует собственную кодировку русских букв, которую принято назвать по номеру кодовой страницы Windows-1251 или CP1251: Символьные константы состоят из одного символа кода ASCII, заключенного в апострофы ’A’ ’s’ Строки символов это массивы байтовых чисел - символов ASCII, заключенных в двойные кавычки. Транслятор помешает в конец каждой строки нулевой байт \0 ”This is a character string” ”A” ”1234” 3. Числа с плавающей точкой Для использования чисел с дробной частью, а также для расширения диапазона их представления вводится форма представления чисел с плавающей точкой Как известно, любое число можно представить в виде X=m*10 в степени p, где 0.1 < m < 1 - значащая часть числа, приведенная к интервалу 0.1 - 1, a p порядок числа Аналогичная форма двоичного числа имеет вид X=m*2 в степени p, где 0.5 < m < 1 - мантисса, a p - двоичный порядок Число с плавающей точкой можно представить в виде двух целых чисел со знаком (m и p), причем p - обычное целое со знаком, а m - представление дробной части, в которой десятичная точка считается расположенной после знакового разряда числа. Не вдаваясь в подробности выполнения операций с плавающей точкой, нужно заметить, что реализовать их можно как аппаратно, так и программно с использованием операций над обычными целыми со знаком В Си имеется два типа данных для чисел с плавающей точкой: обычная (float) и двойной точности (double). Перед использованием в выражении любая переменная типа float преобразуется к double Для совместимости с программами написанными на языке Pascal добавлен еше один тип long double float Переменной типа float компилятор отводит 4 байта памяти Eе численное значение хранится в нормализованном виде. Нормализация состоит в сдвиге значащих бит двоичного кода числа влево или вправо до тех пор, пока целая часть числа не станет равной единице. Разумеется, для сохранения численного значения каждая операция сдвига сопровождается соответствующим изменением двоичного порядка числа Но если целая часть нормализованного двоичного числа всегда равна единице, ее можно вообще не хранить в ячейке памяти! Это экономит один "лишний" бит для записи мантиссы, увеличивая точность представления чисел. Конечно, при чтении числа из ячейки памяти такая "неявная единица", естественно, автоматически восстанавливается float Порядок, полученный при нормализации, перед записью в ячейку памяти несколько видоизменяется. К нему прибавляется фиксированное целое число так, чтобы он всегда был неотрицательным. Такой искусственно смещенный порядок называется характеристикой. Это избавляет от необходимости выделять специальный бит для хранения знака порядка и упрощает процедуру сравнения порядков чисел при выполнении над ними арифметических действий Для чисел типа float прибавляется число 127, для чисел типа double прибавляется 1023, для чисел типа long double прибавляется 32773 float Структура ячейки памяти вещественной переменной типа float имеет вид: 4 байта <-----¦----------------¦--------------------------------------->¦ ¦1 бит¦ 8 бит ¦ 23 бита ¦ ¦знак ¦ Характеристика ¦ Мантисса ¦ ¦-----¦----------------¦----------------------------------------¦ В качестве примера рассмотрим запись в такую ячейку числа 15.375: 1111.011 - двоичный код числа Сдвигаем десятичную точку влево на три бита: 1.111011 * 2 в степени 3 - нормализованное число Получим из порядка характеристику: 3 + 127 = 130 или 1000 0010 в двоичной записи Учитывая, что число положительное, заполняем 4 байта памяти: ¦0¦100 0001 0¦111 0110 0000 0000 0000 0000¦ 4 1 7 6 0 0 0 0 Итак, если "заглянуть" в ячейку памяти, где хранится число 15.375 , то мы "увидим" двоичный код 41760000h. Минимальное значение |x| >= 1.0E-38 Максимальное значение |x| <= 1.0E+38 <-----¦----------------¦--------------------------------------->¦ ¦1 бит¦ 8 бит ¦ 23 бита ¦ ¦знак ¦ Характеристика ¦ Мантисса ¦ ¦-----¦----------------¦----------------------------------------¦ ¦0¦100 0001 0¦111 0110 0000 0000 0000 0000¦ 4 1 7 6 0 0 0 0 ¦0¦1 000 001 0¦11 101 100 000 000 000 000 000¦ 0 0 0 0 0 0 0 0 0 0 0 Итак, если "заглянуть" в ячейку памяти, где хранится число 15.375 , то мы "увидим" двоичный код 41760000h. Минимальное значение |x| >= 1.0E-38 Максимальное значение |x| <= 1.0E+38 double Структура ячейки памяти вещественной переменной типа double имеет вид: 8 байт <-----¦----------------¦--------------------------------------->¦ ¦1 бит¦ 11 бит ¦ 52 бита ¦ ¦знак ¦ Характеристика ¦ Мантисса ¦ ¦-----¦----------------¦----------------------------------------¦ Для вычисления характеристики прибавляется 1023. Увеличены как диапазон, так и точность представления вещественных чисел Минимальное значение |x| >= 1.0E-308 Максимальное значение |x| <= 1.0E+308 long double Структура ячейки памяти вещественной переменной типа long double имеет вид: 10 байт <-----¦----------------¦--------------------------------------->¦ ¦1 бит¦ 16 бит ¦ 63 бита ¦ ¦знак ¦ Характеристика ¦ Мантисса ¦ ¦-----¦----------------¦----------------------------------------¦ Для вычисления характеристики прибавляется 32773. Увеличены как диапазон, так и точность представления вещественных чисел Минимальное значение |x| >= 1.0E-4932 Максимальное значение |x| <= 1.0E+4932 15.375 = |0|100 0000 0000 0010 1|111 0110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 4 0 0 2 15.375 = f 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x 4002f600000000000000h Арифметические операции над вещественными числами обычно выполняет специальный сопроцессор, который обычно встроен в основную схему процессора Если он отсутствует, его работа моделируется программным способом с помощью функций стандартной библиотеки (эмуляция сопроцессора). Это существенно снижает скорость работы программы Следующая программа позволяет посмотреть двоичное представление в памяти компьютера стандартных данных. Для завершения программы необходимо нажать Ctrl/C Пример программы Диапазон базовых типов сhar int short long Символьная со знаком (1) от -128 до 127 Целая (2) от -32768 до 32767 Короткая целая (2) от -32768 до 32767 Длинная целая (4) от -2147483648 до 2147483647 unsigned char Символьная без знака (1) от 0 до 255 unsigned Целая без знака (2) от 0 до 65535 unsigned short Короткая целая без знака (2) от 0 до 65535 unsigned long Длинная целая без знака (4) от 0 до 4294967297 float double Вещественная (4) |x| >= 1.0E-38 |x| <= 1.0E+38 Вещественная двойной (8) |x| >= 1.0E-308 |x| <= 1.0E+308