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

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
м Замена <tt /> на <code />; избыточные <big /> и <font /> вокруг <source />; {{SUBPAGENAME}}; пробелы.
Выделены три раздела; дополнения и удаления (NB: материал следует выборочно вернуть); ссылки на стандарт.
Строка 1: Строка 1:
{{Содержание «Язык Си в примерах»}}
{{{{BASEPAGENAME}}/Содержание}}


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


== Вариант «простой» ==
В нашей программе будут две целочисленные переменные: <code>а</code> и <code>b</code>. Это

две ячейки памяти, в которых могут храниться целые числа из определенного диапазона значений
В отличие от рассмотренной ранее [[../Простейшая программа «Hello World»|простейшей программы]], в данной задаче нам потребуются [[w:Переменная (программирование)|переменные]] — ячейки памяти, в которые функция ввода сохранит введенные пользователем числа, подлежащие сложению.
(в [[w:Машинное слово|32-разрядной архитектуре]] от <math>-2^{31}</math> до <math>2^{31}-1</math>).

Как и функции (в том числе <code>main</code>), объявление переменных в Си предполагает указание их типов. Основные числовые типы языка — <code>int</code> ([[w:Целое число|целые числа]] фиксированной разрядности) и <code>double</code> ([[w:Число с плавающей запятой|числа с плавающей запятой]].) Поскольку мы уже использовали тип <code>int</code> в описании функции <code>main</code>, применим его же в данной задаче.


Переменные объявляются в начале тела функции <code>main</code> &mdash; после открывающей фигурной скобки.
Объявление начинается со слова, обозначающего тип переменных, имена которых перечисляются через запятую
после обозначения типа.
<source lang="c">
<source lang="c">
#include <assert.h>
int a, b;
#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;
}
</source>
</source>


Рассмотрим выполнение этой программы почти с ее завершения — вызова функции <code>printf</code>.<ref name="fprintf" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=327 WG14 N1570 Committee Draft — April 12, 2011] 7.21.6.1 ''The fprintf function''</ref> Данная функция выведет целое число в десятичной форме (согласно ''указателю преобразования'' <code>%d</code>), завершая вывод [[w:Перевод строки|переводом строки]] (<code>\n</code>).
В языке Си есть несколько типов числовых данных. Они делятся на две группы: целые числа и числа с плавающей точкой.


Число, которое будет выведено, является результатом вычисления выражения <code>a + b</code> — или же, проще говоря, — суммой значений переменных <code>a</code> и <code>b</code>.
К первой группе относятся:
* <code>char</code> — один байт, обычно 8 бит.
* <code>short</code> — короткое число, чаще всего в 2 раза короче, чем int.
* <code>int</code> — обычное число, на x86 32 бита.
* <code>long</code> — длинное число, на x86 32 бита, но на 64-битных x86-системах в зависимости от компилятора может быть как 32 бита, так и 64.
* <code>long long</code> — очень длинное число, обычно 64 бита.


Вводимые пользователем числа помещаются в эти переменные функцией <code>scanf</code>, вызываемой в коде выше как <code>scanf ("%d%d", &a, &b)</code>.<ref name="fscanf" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=335 WG14 N1570 Committee Draft — April 12, 2011] 7.21.6.2 ''The fscanf function''</ref> Здесь, как и в <code>printf</code>, используется указатель преобразования <code>%d</code>, означающий на этот раз ''считывание'' числа в десятичной форме (возможно — предваряемого пробелами). Поскольку указатель повторен дважды, будет считано два числа, которые будут помещены в упомянутые в аргументах переменные <code>a</code> и <code>b</code>. (Необходимый здесь унарный оператор <code>&</code> оставим пока без внимания.)
Целые числа могут быть знаковые (как положительные, так и отрицательные) и беззнаковые (только положительные). По умолчанию числа знаковые, и чтобы сделать их беззнаковыми, необходимо написать слово <code>unsigned</code> перед названием типа.


Как и <code>printf</code>, функция <code>scanf</code> ''объявлена'' в ''заголовке'' (англ. {{lang|en|header}}) <code>stdio.h</code>.<ref name="stdio.h" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=314 WG14 N1570 Committee Draft — April 12, 2011] 7.21 ''Input/output <code>stdio.h</code>''</ref>
Точный размер целых чисел указан в заголовочном файле <code>limits</code>.


В [[../Простейшая программа «Hello World»|простейшей программе]] от действий пользователя не зависело ровным счетом ничего. Каким, однако, будет результат выполнения данной программы, если ввод пользователя не будет начат двумя числами в десятичной форме?
Ко второй группе относятся:
* <code>float</code> — 32 бита.
* <code>double</code> — 64 бита.


Для проверки соответствия ввода пользователя требованиям программы мы ''сохраняем'' (<code>=</code>) результат выполнения <code>scanf</code> — количество успешно измененных переменных — в целочисленной переменной с именем <code>r</code> (<code>int r</code>), после чего ''требуем'' равенства ее значения двум (<code>assert (r == 2);</code>.)
Особые величины этих типов даны в заголовке <code>float</code>.


<small>Действие ''макроподстановки'' <code>assert</code> заключается в сравнении результата вычисления выражения, переданного ей первым (и единственным) аргументом и ''аварийном завершении'' программы в случае, если полученное значение — ноль («логическая ложь».)<ref name="assert" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=204 WG14 N1570 Committee Draft — April 12, 2011] 7.2.1.1 ''The assert macro''</ref></small>
Вот текст программы, складывающей два введенных целых числа:
<source lang="c">
#include <stdio.h>
int main () {
int a, b;
printf ("Введите два числа: \n");
scanf ("%d%d", &a, &b);
printf ("%d\n", a + b);
return 0;
}
</source>


Наконец, в самом начале функции <code>main</code> ''определены'' (помимо упомянутой уже <code>r</code>) целочисленные переменные <code>a</code> и <code>b</code>. Их значение в начале выполнения функции <code>main</code> может быть произвольным,<ref name="init" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=157 WG14 N1570 Committee Draft — April 12, 2011] 6.7.9 ''Initialization''</ref> но после успешного (что проверяется вызовом <code>assert</code>) завершения <code>scanf</code> они будут содержать два числа, которые удалось последовательно считать со [[w:Стандартные потоки#Стандартный ввод|стандартного ввода]].
Функция <code>scanf</code>, также как и <code>printf</code>, определена в библиотеке <code>stdio</code>.
Эта функция считывает данные, которые пользователь (тот, кто
запустит вашу программу) вводит с клавиатуры. Слово <code>scan</code>
означает «считывать данные», а <code>print</code> &mdash; «печатать данные».
Буква «f» в конце соответствует первой букве английского слова
«formatted», то есть <code>scanf</code> и <code>printf</code> есть функции для
форматированного ввода и вывода данных.


== Вариант «дробный» ==
Первый аргумент у функции <code>scanf</code> &mdash; это <code>"%d%d"</code> (то, что стоит между открывающей скобкой и первой запятой).
Первый аргумент является описанием формата входных данных, то есть описание типа данных, которые (как мы ожидаем) введёт пользователь.
Второй и третий аргументы являются указателями (создаются символом «&amp;») на переменные <code>a</code> и <code>b</code>. Указатели нужны для того, чтобы передать функции не значения этих переменных, а их адреса — местоположения в памяти, по которым функция будет записывать значения.
В этой программе мы ожидаем, что пользователь введет два целых числа.


Рассмотренную [[#Вариант «простой»|выше]] программу несложно изменить для использования [[w:Число с плавающей запятой|чисел с плавающей запятой.]]
Символ % служебный, с него начинается описание формата.
Обычно, после него идет один или два символа, определяющих тип входных данных. Формат "%d" соответствует целому числу в десятичной системе счисления (decimal integer). Если вы напишете "%x", то функция будет ожидать ввода целого числа, записанного в шестнадцатиричной системе счисления.


<source lang="c">
Подробнее об спецификациях форматах ввода/вывода можно прочитать в документации (для Unix систем):
#include <assert.h>
#include <stdio.h>


int
bash$ man 3 printf
main ()
bash$ man 3 scanf
{
double a, b;
int r
= scanf ("%lg%lg", &a, &b);
assert (r == 2);
printf ("%lg\n", a + b);
return 0;
}
</source>

Как видно, в этом случае изменяются лишь тип переменных <code>a</code>, <code>b</code> (<code>int</code> → <code>double</code>) и указатели преобразований (<code>%d</code> → <code>%lg</code>.)


<small>Здесь следует отметить, что в случае <code>scanf</code> совершенно идентично будут действовать указатели преобразований <code>%lf</code> и <code>%le</code>. Напротив, в случае <code>printf</code> не будет разницы между <code>%lg</code> и <code>%g</code>. Причины такого поведения мы также пока оставим без внимания.</small>
Первый аргумент команды <code>man</code> есть номер раздела документации.
Помощь по языкам Си/Си++ находится в третьем разделе.


== Вариант «полный» ==
Следует отметить, что для каждого типа данных существует несколько форматов, и наоборот,
для разных типов можно использовать один и тот же формат{{ref|char}}.


Приведенная программа умеет складывать только целые числа.
Если вы хотите складывать действительные числа, то эту программу нужно несколько модифицировать.
Ниже приведена программа, которая считывает два действительных числа и
Ниже приведена программа, которая считывает два действительных числа и
выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления.
выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления.
Причём, программа выводит результаты вычислений два раза &mdash; сначала
Причём, программа выводит результаты вычислений два раза сначала
в обычном виде, а потом со специальным форматированием.
в обычном виде, а потом со специальным форматированием.
Формат <code>"%10.3lf"</code> соответствует выводу числа типа <code>double</code>,
Формат <code>"%10.3lf"</code> соответствует выводу числа типа <code>double</code>,
Строка 128: Строка 121:


Заметьте, что после каждой команды стоит точка с запятой.
Заметьте, что после каждой команды стоит точка с запятой.
Одна из самых популярных синтаксических ошибок начинающих программистов &mdash; это не ставить точку c запятой в конце команды.
Одна из самых популярных синтаксических ошибок начинающих программистов это не ставить точку c запятой в конце команды.


==Примечания==
==Примечания==


{{Примечания}}
# {{note|char}} Переменные типа <code>char</code>, <code>short</code>, <code>int</code> можно печатать, используя формат "%d". Также этот формат можно использовать для печати значений [[Язык Си в примерах/Указатели в языке Си|указателей]] (номера ячейки памяти).

[[Категория:Язык Си в примерах|{{SUBPAGENAME}}]]
[[Категория:Язык Си в примерах|{{SUBPAGENAME}}]]

Версия от 23:59, 29 января 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. Причины такого поведения мы также пока оставим без внимания.

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

Ниже приведена программа, которая считывает два действительных числа и выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления. Причём, программа выводит результаты вычислений два раза — сначала в обычном виде, а потом со специальным форматированием. Формат "%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 запятой в конце команды.

Примечания