Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Использование полученных знаний на практике
Итак, вы ознакомились с несколькими примерами. Как должен выглядеть оператор для вывода текста в следующей форме:
Семья NAME может стать богаче на $ХХХ.ХХ!
138 Глава 4
Здесь NAME и XXX. XX представляют значения, которые будут предоставляться в программе переменными, скажем, name [40] и cash.
Одно из возможных решений выглядит гак:
printf("Семья %s может стать богаче на $%.2f!\n", name, cash);
Что поеобоазует спецификатоо поеобоазования?
Теперь более подробно рассмотрим, что именно преобразует спецификатор преобразования. Он преобразует значение, хранящееся в памяти компьютера в двоичном формате, в последовательность символов (строку) с целью отображения. Например, число 76 может быть представлено в памяти компьютера в двоичном виде как 01001100. Спецификатор преобразования %d превращает его в символы 7 и 6, отображая 76. Преобразование %х превращает это же двоичное значение (01001100) в шестнадцатеричное представление 4с, а спецификатор %с преобразует его в символьное представление L.
Термин преобразование, возможно, в чем-то неточен, т.к. можно предположить, что исходное значение заменяется преобразованным. Спецификаторы преобразования по существу являются спецификаторами трансляции; к примеру, %d означает “транслировать заданное значение в десятичное целочисленное текстовое представление и затем вывести его”.
Несовпадающие преобразования
Естественно, спецификатор преобразования должен соответствовать типу выводимого значения. Часто вам доступно несколько вариантов. Например, для вывода значения типа int можно применять спецификатор %d, %х или %o. Все эти спецификаторы предполагают, что вы выводите значение типа int; они просто предоставляют различные представления этого значения. Аналогично, спецификаторы %f, %е или %д можно использовать для представления типа double.
Что произойдет, если спецификатор преобразования не соответствует типу? В предыдущей главе вы уже видели, что несоответствия могут вызвать проблемы. Это очень важный аспект, который следует иметь в виду, так что в листинге 4.11 приведено еще несколько примеров несоответствия при работе с семейством целочисленных типов.
Листинг 4.11. Программа intconv.c
Символьные строки и форматированный ввод-вывод 139
В нашей системе были получены следующие результаты:
num как тип short и тип unsigned short: 336 336 -num как тип short и тип unsigned short: -336 65200 num как тип int и тип char: 336 Р WORDS тип int, short и char: 65618 82 R
Взглянув на первую строку, вы можете заметить, что спецификаторы %hd и %hu выдают 336 в качестве вывода для переменной num; туг нет никаких проблем. Однако во второй строке версия %u (без знака) для mnum выглядит как 65200, а не как ожидаемое значение 336; это вытекает из способа представления значений типа short int со знаком в нашей системе. Во-первых, они имеют размер 2 байта. Во-вторых, для представления целых чисел со знаком система использует метод, называемый поразрядным дополнением до двойки. При таком методе числа от 0 до 32767 представляют сами себя, а числа от 32768 до 65535 представляют отрицательные числа, причем 65535 соответствует -1, 65534 2 и т.д. Следовательно, -336 представлено как 65536 - 336, или
65200. Таким образом, число 65200 представляет -336, когда интерпретируется как int со знаком, и 65200, когда интерпретируется как int без знака. Поэтому будьте осторожны! Одно число может интерпретироваться как два разных значения. Описанный метод представления отрицательных целых чисел применяется не во всех системах. Тем не менее, мораль этого примера: не рассчитывайте на то, что преобразование %u просто отбросит знак числа.
Третья строка демонстрирует, что происходит при попытке преобразования в символ значения, которое больше 255. В нашей системе тип short int занимает 2 байта, а тип char — 1 байт. Когда функция printf() выводит 336 с использованием спецификатора %с, она просматривает только один байт из двух, задействованных для хранения 336. Такое усечение (рис. 4.8) равнозначно делению целого числа на 256 с сохранением только остатка. В этом случае остаток равен 80, что представляет собой ASCII-значение символа Р. Формально можно сказать, что число интерпретируется как результат деления по модулю 256, что означает использование остатка от деления числа на 256.
Рис. 4.8. Интерпретация числа 336 как символа
В заключение мы попытались вывести в своей системе целое число (65618), превышающее максимально допустимое значение типа short int (32767). И снова компьютер применил деление по модулю. Число 65618 в силу своего размера хранится в нашей системе как 4-байтовое значение int. Когда оно выводится с применением спецификатора %hd, функция printf() использует только последние 2 байга, которые равносильны остатку от деления на 65536. В этом случае остаток равен 82. Остаток, находящийся между 32767 и 65536, с учетом способа хранения отрицательных чисел выводился бы как отрицательное число. В системах с другими размерами целых чисел общее поведение было бы таким же, но с другими числовыми значениями.
140 Глава 4
Когда вы начнете смешивать целочисленные типы и типы с плавающей запятой, результаты станут еще более причудливыми. Для примера рассмотрим программу, приведенную в листинге 4.12.
Листинг 4.12. Программа floatcnv.c
В нашей системе код из листинга 4.12 сгенерировал следующий вывод:
3.0е+00 3.0е+00 3.1е+46 1.7е+266
2000000000 1234567890
0 1074266112 0 1074266112
Первая строка вывода показывает, что применение спецификатора %е не вызывает преобразование целого числа в число с плавающей запятой. Давайте посмотрим, что происходит при попытке вывода переменной n3 (типа long) с использованием спецификатора %е. Во-первых, спецификатор %е заставляет функцию printf() ожидать значение типа double, которое в нашей системе является 8-байтовым. Когда функция printf() исследует переменную n3, представленную в нашей системе 4-байтовым значением, она просматривает также смежные 4 байта памяти. Таким образом, функция анализирует 8-байтовый блок, в котором содержится действительное значение n3. Во-вторых, она интерпретирует биты этого блока как число с плавающей запятой. Например, некоторые биты, будут трактоваться в качестве показателя степени. Поэтому, даже если бы значение n3 содержало правильное количество битов, для спецификаторов %е и %ld они бы интерпретировались по-разному. Конечный результат оказывается бессмысленным.
Первая строка вывода также иллюстрирует то, что упоминалось ранее — при передаче в виде аргумента функции printf() значение float преобразуется в тип double. В данной системе тип float занимает 4 байта, но переменная nl была расширена до 8 байтов, чтобы функция printf() смогла корректно отобразить ее значение.
Читать дальшеИнтервал:
Закладка: