Язык Си в примерах/Учимся складывать: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Выделены три раздела; дополнения и удаления (NB: материал следует выборочно вернуть); ссылки на стандарт.
→‎Числовые типы: Новый раздел.
Строка 68: Строка 68:


<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⁻9;
* [[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> — десятичная [[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> также могут быть опущены.


== Вариант «полный» ==
== Вариант «полный» ==

Версия от 02:46, 30 января 2014

Язык Си в примерах


  1. Компиляция программ
  2. Простейшая программа «Hello World»
  3. Учимся складывать
  4. Максимум
  5. Таблица умножения
  6. ASCII-коды символов
  7. Верхний регистр
  8. Скобочки
  9. Факториал
  10. Степень числа
  11. Треугольник Паскаля
  12. Корень уравнения
  13. Система счисления
  14. Сортировка
  15. Библиотека complex
  16. Сортировка на основе qsort
  17. RPN-калькулятор
  18. RPN-калькулятор на Bison
  19. Простая грамматика
  20. Задача «Расчёт сопротивления схемы»
  21. Простая реализация конечного автомата
  22. Использование аргументов командной строки
  23. Чтение и печать без использования stdio
  24. Декодирование звукозаписи в формате ADX
  25. Другие примеры

Разнообразные вычисления — моделирование, решение алгебраических и дифференциальных уравнений — это то, для чего и создавались первые компьютеры. Давайте и мы научимся использовать компьютер для вычислений. Начнём со сложения двух чисел.

Вариант «простой»

В отличие от рассмотренной ранее простейшей программы, в данной задаче нам потребуются переменные — ячейки памяти, в которые функция ввода сохранит введенные пользователем числа, подлежащие сложению.

Как и функции (в том числе 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 (intdouble) и указатели преобразований (%d%lg.)

Здесь следует отметить, что в случае scanf совершенно идентично будут действовать указатели преобразований %lf и %le. Напротив, в случае printf не будет разницы между %lg и %g. Причины такого поведения мы также пока оставим без внимания.

Числовые типы

Список числовых типов, определяемых стандартом, следующий (некоторые из этих типов могут иметь более одного стандартного названия; приведены кратчайшие):[6][7]

  • целочисленные:
    • _Bool — логическая величина (0 или 1), не менее одного бита;
    • signed char — байт, не менее 8 бит;
    • short — короткое целое, не менее 16 бит;
    • int — целое, не менее 16 бит;
    • long — длинное целое, не менее 32 бит;
    • long long — длинное длинное целое, не менее 64 бит;
  • с плавающей запятой:
    • float — диапазон: не хуже 10⁻³⁷ … 10⁺³⁷, εмаш < 10⁻⁵;
    • double, long double — диапазон: не хуже 10⁻³⁷ … 10⁺³⁷, εмаш < 10⁻9;
  • комплексные: float _Complex, double _Complex, long double _Complex — диапазон и εмаш действительной и мнимой частей совпадают с таковыми для соответствующих чисел с плавающей запятой.

В случае, если используемая система реализует стандарт IEC 60559 на числа с плавающей запятой, типы с плавающей запятой имеют следующие параметры:

  • float — соответствует представлению «одинарной точности» IEC 60559; диапазон: 1,18 × 10⁻³⁸ … 3,40 × 10⁺³⁸, εмаш = 1.19 × 10⁻⁷;
  • double — «двойная точность» IEC 60559; диапазон: 2,225 × 10⁻³⁰⁸ … 1.80 × 10⁺³⁰⁸, εмаш = 2.22 × 10⁻¹⁶;
  • long double — «четверная точность» IEC 60559; диапазон: 3,36 × 10⁻⁴⁹³² … 1,19 × 10⁺⁴⁹³², εмаш = 1.08 × 10⁻¹⁹.

Кроме того, из целочисленных типов могут быть образованы беззнаковые («неотрицательные») добавлением unsigned перед именем типа. (Исключение: signed char переходит в unsigned char.)

При преобразовании между текстовым (строковым) представлением и этими типами, используются, в частности, следующие указатели преобразований, состоящие из знака %, указателя разрядности, и указателя представления.

Указатели представлений
Целые числа
  • d, i — десятичная запись со знаком (42);
  • u — десятичная запись без знака (42);
  • o — восьмеричная запись (52);
  • x, X — шестнадцатеричная запись (2a, 2A).
Числа с плавающей запятой
  • f — десятичная запись (1000000.000000);
  • e — десятичная E-запись (1.000000e+06, 1.000000E+06);
  • g — «автоматический» выбор между e и f;
  • a, A — шестнадцатеричная запись (0xf.4240p4, 0XF.4240P4).
Указатели разрядности
Целые числа
  • hh — для типов signed char и unsigned char;
  • h — для типов short и unsigned short;
  • (пустая строка) — для типов int и unsigned int
  • l — для типов long и unsigned long;
  • ll — для типов long long и unsigned long.
Числа с плавающей запятой
  • (пустая строка) — для типа float;
  • l — для типа double;
  • L — для типа long double.
Примеры
  • %hx — значение типа unsigned short в шестнадцатеричной записи;
  • %le — значение типа double в десятичной E-записи.

Отметим, что в контексте функций ряда printf, указатель разрядности l не играет никакой роли, если применяется к числу с плавающей запятой. Так, вызовы printf ("%lg", a); и printf ("%g", a); совершенно равнозначны. При передаче таким функциям значений типов signed char, short, и соответствующих беззнаковых, — указатели разрядности hh и h также могут быть опущены.

Вариант «полный»

Ниже приведена программа, которая считывает два действительных числа и выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления. Причём, программа выводит результаты вычислений два раза — сначала в обычном виде, а потом со специальным форматированием. Формат "%10.3lf" соответствует выводу числа типа double, при котором под запись числа выделяется ровно 10 позиций (если это возможно), а после запятой пишется ровно три знака. Равнение происходит по правому краю.

 /* Программа "Арифметические операции с числами с плавающей точкой" */
 #include <stdio.h>
 int main () {
    double a, b;
    printf ("Введите два числа: ");
    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 запятой в конце команды.

Примечания