Язык Си в примерах/Учимся складывать: различия между версиями
→Вариант «полный»: Исключен вывод «подсказки» в примере; оформление фрагментов кода. |
→Числовые типы: Раздел перенесен в Язык Си в примерах/Скалярные типы#Числовые типы. |
||
Строка 69: | Строка 69: | ||
<small>Здесь следует отметить, что в случае <code>scanf</code> совершенно идентично будут действовать указатели преобразований <code>%lf</code> и <code>%le</code>. Напротив, в случае <code>printf</code> не будет разницы между <code>%lg</code> и <code>%g</code>. Причины такого поведения мы также пока оставим без внимания.</small> |
<small>Здесь следует отметить, что в случае <code>scanf</code> совершенно идентично будут действовать указатели преобразований <code>%lf</code> и <code>%le</code>. Напротив, в случае <code>printf</code> не будет разницы между <code>%lg</code> и <code>%g</code>. Причины такого поведения мы также пока оставим без внимания.</small> |
||
Желающим изучить использование других числовых типов в этой задаче предлагается обратиться к разделу [[Язык Си в примерах/Скалярные типы#Числовые типы|Числовые типы]] приложения. |
|||
== Числовые типы == |
|||
Список числовых типов, определяемых стандартом, следующий (некоторые из этих типов могут иметь более одного стандартного названия; приведены кратчайшие):<ref name="types" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=57 WG14 N1570 Committee Draft — April 12, 2011] 6.2.5 ''Types''</ref><ref name="limits" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=523 WG14 N1570 Committee Draft — April 12, 2011] Annex E (informative) ''Implementation limits''</ref> |
|||
* [[w:Целое число|целочисленные]]: |
|||
** <code>_Bool</code> — логическая величина (0 или 1), не менее одного бита; |
|||
** <code>signed char</code> — байт, не менее 8 бит; |
|||
** <code>short</code> — короткое целое, не менее 16 бит; |
|||
** <code>int</code> — целое, не менее 16 бит; |
|||
** <code>long</code> — длинное целое, не менее 32 бит; |
|||
** <code>long long</code> — длинное длинное целое, не менее 64 бит; |
|||
* [[w:Число с плавающей запятой|с плавающей запятой]]: |
|||
** <code>float</code> — диапазон: не хуже 10⁻³⁷ … 10⁺³⁷, [[w:Число с плавающей запятой#Машинная эпсилон|''ε''<sub>маш</sub>]] < 10⁻⁵; |
|||
** <code>double</code>, <code>long double</code> — диапазон: не хуже 10⁻³⁷ … 10⁺³⁷, ''ε''<sub>маш</sub> < 10⁻⁹; |
|||
* [[w:Комплексное число|комплексные]]: <code>float _Complex</code>, <code>double _Complex</code>, <code>long double _Complex</code> — диапазон и ''ε''<sub>маш</sub> действительной и мнимой частей совпадают с таковыми для соответствующих чисел с плавающей запятой. |
|||
В случае, если используемая система реализует стандарт [[w:IEEE 754-2008|IEC 60559]] на числа с плавающей запятой, типы с плавающей запятой имеют следующие параметры: |
|||
* <code>float</code> — соответствует представлению «[[w:Число одинарной точности|одинарной точности]]» IEC 60559; диапазон: 1,18 × 10⁻³⁸ … 3,40 × 10⁺³⁸, ''ε''<sub>маш</sub> = 1.19 × 10⁻⁷; |
|||
* <code>double</code> — «[[w:Число двойной точности|двойная точность]]» IEC 60559; диапазон: 2,225 × 10⁻³⁰⁸ … 1.80 × 10⁺³⁰⁸, ''ε''<sub>маш</sub> = 2.22 × 10⁻¹⁶; |
|||
* <code>long double</code> — «[[w:Число четверной точности|четверная точность]]» IEC 60559; диапазон: 3,36 × 10⁻⁴⁹³² … 1,19 × 10⁺⁴⁹³², ''ε''<sub>маш</sub> = 1.08 × 10⁻¹⁹. |
|||
Кроме того, из целочисленных типов могут быть образованы ''беззнаковые'' («неотрицательные») добавлением <code>unsigned</code> перед именем типа. (Исключение: <code>signed char</code> переходит в <code>unsigned char</code>.) |
|||
При преобразовании между текстовым (строковым) представлением и этими типами, используются, в частности, следующие указатели преобразований, состоящие из знака <code>%</code>, ''указателя разрядности'', и ''указателя представления''. |
|||
; Указатели представлений |
|||
:; Целые числа |
|||
::* <code>d</code>, <code>i</code> — десятичная запись со знаком (42); |
|||
::* <code>u</code> — десятичная запись без знака (42); |
|||
::* <code>o</code> — восьмеричная запись (52); |
|||
::* <code>x</code>, <code>X</code> — шестнадцатеричная запись (2a, 2A). |
|||
:; Числа с плавающей запятой |
|||
::* <code>f</code> — десятичная запись (1000000.000000); |
|||
::* <code>e</code>, <code>E</code> — десятичная [[w:Экспоненциальная запись#Компьютерный способ экспоненциальной записи|E-запись]] (1.000000e+06, 1.000000E+06); |
|||
::* <code>g</code> — «автоматический» выбор между <code>e</code> и <code>f</code>; |
|||
::* <code>a</code>, <code>A</code> — шестнадцатеричная запись (0xf.4240p4, 0XF.4240P4). |
|||
; Указатели разрядности |
|||
:; Целые числа |
|||
::* <code>hh</code> — для типов <code>signed char</code> и <code>unsigned char</code>; |
|||
::* <code>h</code> — для типов <code>short</code> и <code>unsigned short</code>; |
|||
::* (пустая строка) — для типов <code>int</code> и <code>unsigned int</code> |
|||
::* <code>l</code> — для типов <code>long</code> и <code>unsigned long</code>; |
|||
::* <code>ll</code> — для типов <code>long long</code> и <code>unsigned long</code>. |
|||
:; Числа с плавающей запятой |
|||
::* (пустая строка) — для типа <code>float</code>; |
|||
::* <code>l</code> — для типа <code>double</code>; |
|||
::* <code>L</code> — для типа <code>long double</code>. |
|||
; Примеры |
|||
:* <code>%hx</code> — значение типа <code>unsigned short</code> в шестнадцатеричной записи; |
|||
:* <code>%le</code> — значение типа <code>double</code> в десятичной E-записи. |
|||
Отметим, что в контексте функций ряда <code>printf</code>, указатель разрядности <code>l</code> не играет никакой роли, если применяется к числу с плавающей запятой. Так, вызовы <code>printf ("%lg", a);</code> и <code>printf ("%g", a);</code> совершенно равнозначны. При передаче таким функциям значений типов <code>signed char</code>, <code>short</code>, и соответствующих беззнаковых, — указатели разрядности <code>hh</code> и <code>h</code> также могут быть опущены. |
|||
== Вариант «полный» == |
== Вариант «полный» == |
Версия от 10:36, 1 февраля 2014
- Компиляция программ
- Простейшая программа «Hello World»
- Учимся складывать
- Максимум
- Таблица умножения
- ASCII-коды символов
- Верхний регистр
- Скобочки
- Факториал
- Степень числа
- Треугольник Паскаля
- Корень уравнения
- Система счисления
- Сортировка
- Библиотека complex
- Сортировка на основе qsort
- RPN-калькулятор
- RPN-калькулятор на Bison
- Простая грамматика
- Задача «Расчёт сопротивления схемы»
- Простая реализация конечного автомата
- Использование аргументов командной строки
- Чтение и печать без использования stdio
- Декодирование звукозаписи в формате ADX
- Другие примеры
Разнообразные вычисления — моделирование, решение алгебраических и дифференциальных уравнений — это то, для чего и создавались первые компьютеры. Давайте и мы научимся использовать компьютер для вычислений. Начнём со сложения двух чисел.
Вариант «простой»
В отличие от рассмотренной ранее простейшей программы, в данной задаче нам потребуются переменные — ячейки памяти, в которые функция ввода сохранит введенные пользователем числа, подлежащие сложению.
Как и в случае функций (в том числе main
), объявление переменных в Си предполагает указание их типов. Основные числовые типы языка — int
(целое число фиксированной разрядности) и double
(число с плавающей запятой.) Поскольку мы уже использовали тип int
в описании функции main
, применим его же в данной задаче.
#include <assert.h>
#include <stdio.h>
int
main ()
{
int a, b;
int r
= scanf ("%d%d", &a, &b);
assert (r == 2);
printf ("%d\n", a + b);
return 0;
}
Рассмотрим выполнение этой программы почти с ее завершения — вызова функции printf
.[1] Данная функция выведет целое число в десятичной форме (согласно указателю преобразования %d
), завершая вывод переводом строки (\n
).
Число, которое будет выведено, является результатом вычисления выражения a + b
— или же, проще говоря, — суммой значений переменных a
и b
.
Вводимые пользователем числа помещаются в эти переменные функцией scanf
, вызываемой в коде выше как scanf ("%d%d", &a, &b)
.[2] Здесь, как и в printf
, используется указатель преобразования %d
, означающий на этот раз считывание числа в десятичной форме (возможно — предваряемого пробелами). Поскольку указатель повторен дважды, два числа будут считаны и помещены в упомянутые в аргументах две переменные a
и b
. (Необходимый здесь унарный оператор &
оставим пока без внимания.)
Как и printf
, функция scanf
объявлена в заголовке (англ. header) stdio.h
.[3]
В простейшей программе от действий пользователя не зависело ровным счетом ничего. Каким, однако, будет результат выполнения данной программы, если ввод пользователя не будет начат двумя числами в десятичной форме?
Для проверки соответствия ввода пользователя требованиям программы мы сохраняем (=
) результат выполнения scanf
— количество успешно измененных переменных — в целочисленной переменной с именем r
(int r
), после чего требуем равенства ее значения двум (assert (r == 2);
.)
Действие макроподстановки assert
заключается в вычислении выражения, переданного ей первым (и единственным) аргументом и аварийном завершении программы в случае, если полученное значение — ноль («логическая ложь».)[4]
Наконец, в самом начале функции main
определены (помимо упомянутой уже r
) целочисленные переменные a
и b
. Их значение в начале выполнения функции main
может быть произвольным,[5] но после успешного (что проверяется вызовом assert
) завершения scanf
они будут содержать два числа, которые удалось последовательно считать со стандартного ввода.
Вариант «дробный»
Рассмотренную выше программу несложно изменить для использования чисел с плавающей запятой.
#include <assert.h>
#include <stdio.h>
int
main ()
{
double a, b;
int r
= scanf ("%lg%lg", &a, &b);
assert (r == 2);
printf ("%lg\n", a + b);
return 0;
}
Как видно, в этом случае изменяются лишь тип переменных a
, b
(int
→ double
) и указатели преобразований (%d
→ %lg
.)
Здесь следует отметить, что в случае scanf
совершенно идентично будут действовать указатели преобразований %lf
и %le
. Напротив, в случае printf
не будет разницы между %lg
и %g
. Причины такого поведения мы также пока оставим без внимания.
Желающим изучить использование других числовых типов в этой задаче предлагается обратиться к разделу Числовые типы приложения.
Вариант «полный»
Ниже приведена программа, которая считывает два действительных числа и
выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления.
Причём, программа выводит результаты вычислений два раза — сначала
в обычном виде, а потом со специальным форматированием.
Формат "%10.3lf"
соответствует выводу числа типа double
,
при котором под запись числа выделяется ровно 10 позиций (если это возможно),
а после запятой пишется ровно три знака. Равнение происходит по правому краю.
#include <stdio.h>
int
main ()
{
double a, b;
while (scanf ("%lf%lf", &a, &b) == 2) {
printf ("%lf %lf %lf %lf\n",
a + b, a - b, a * b, a / b);
printf (("a + b = %10.3lf\n"
"a - b = %10.3lf\n"
"a * b = %10.3lf\n"
"a / b = %10.3lf\n"),
a + b, a - b, a * b, a / b);
}
return 0;
}
В этой программе мы встречаемся с оператором while
. Конструкция
while ( A ) B;
означает буквально следующее:
- Пока выполнено условие
A
делатьB
.
или, другими словами,
- Выполнять в цикле
B
, проверяя перед каждой итерацией, что выполнено условиеA
.
В нашем случае A
есть
scanf ("%lf%lf", &a, &b) == 2
Что соответствует логическому выражению:
- пользователь ввёл два действительных числа, и они удачно считаны в переменные a и b.
Таким образом, эта программа будет считывать пары чисел и выводить результаты арифметических операций, пока пользователь не введёт что-нибудь непохожее на число.
Цикл while
закончится тогда, когда функция scanf
не сможет успешно считать два числа.
Заметьте, что после каждой команды стоит точка с запятой. Одна из самых популярных синтаксических ошибок начинающих программистов — это не ставить точку c запятой в конце команды.
Примечания
- ↑ WG14 N1570 Committee Draft — April 12, 2011 7.21.6.1 The fprintf function
- ↑ WG14 N1570 Committee Draft — April 12, 2011 7.21.6.2 The fscanf function
- ↑ WG14 N1570 Committee Draft — April 12, 2011 7.21 Input/output
stdio.h
- ↑ WG14 N1570 Committee Draft — April 12, 2011 7.2.1.1 The assert macro
- ↑ WG14 N1570 Committee Draft — April 12, 2011 6.7.9 Initialization