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

Содержимое удалено Содержимое добавлено
м Замена <tt /> на <code />; избыточные <big /> и <font /> вокруг <source />; {{SUBPAGENAME}}; пробелы.
Выделены три раздела; дополнения и удаления (NB: материал следует выборочно вернуть); ссылки на стандарт.
Строка 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">
#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>
 
Рассмотрим выполнение этой программы почти с ее завершения — вызова функции <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>,
Строка 128 ⟶ 121 :
 
Заметьте, что после каждой команды стоит точка с запятой.
Одна из самых популярных синтаксических ошибок начинающих программистов &mdash; это не ставить точку c запятой в конце команды.
 
==Примечания==
 
{{Примечания}}
# {{note|char}} Переменные типа <code>char</code>, <code>short</code>, <code>int</code> можно печатать, используя формат "%d". Также этот формат можно использовать для печати значений [[Язык Си в примерах/Указатели в языке Си|указателей]] (номера ячейки памяти).
 
[[Категория:Язык Си в примерах|{{SUBPAGENAME}}]]