Язык Си в примерах/Учимся складывать: различия между версиями
ISbot (обсуждение | вклад) м Замена <tt /> на <code />; избыточные <big /> и <font /> вокруг <source />; {{SUBPAGENAME}}; пробелы. |
Выделены три раздела; дополнения и удаления (NB: материал следует выборочно вернуть); ссылки на стандарт. |
||
Строка 1: | Строка 1: | ||
{{Содержание |
{{{{BASEPAGENAME}}/Содержание}} |
||
Разнообразные вычисления |
Разнообразные вычисления — |
||
моделирование, решение алгебраических и дифференциальных уравнений |
моделирование, решение алгебраических и дифференциальных уравнений — |
||
это то, для чего и создавались первые компьютеры. |
это то, для чего и создавались первые компьютеры. |
||
Давайте и мы научимся использовать компьютер для вычислений. |
Давайте и мы научимся использовать компьютер для вычислений. |
||
Начнём со сложения двух чисел. |
Начнём со сложения двух чисел. |
||
== Вариант «простой» == |
|||
В нашей программе будут две целочисленные переменные: <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> — после открывающей фигурной скобки. |
|||
Объявление начинается со слова, обозначающего тип переменных, имена которых перечисляются через запятую |
|||
после обозначения типа. |
|||
<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> — «печатать данные». |
|||
Буква «f» в конце соответствует первой букве английского слова |
|||
«formatted», то есть <code>scanf</code> и <code>printf</code> есть функции для |
|||
форматированного ввода и вывода данных. |
|||
== Вариант «дробный» == |
|||
Первый аргумент у функции <code>scanf</code> — это <code>"%d%d"</code> (то, что стоит между открывающей скобкой и первой запятой). |
|||
Первый аргумент является описанием формата входных данных, то есть описание типа данных, которые (как мы ожидаем) введёт пользователь. |
|||
Второй и третий аргументы являются указателями (создаются символом «&») на переменные <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}}. |
|||
Приведенная программа умеет складывать только целые числа. |
|||
Если вы хотите складывать действительные числа, то эту программу нужно несколько модифицировать. |
|||
Ниже приведена программа, которая считывает два действительных числа и |
Ниже приведена программа, которая считывает два действительных числа и |
||
выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления. |
выводит результат четырех арифметических операций: сложения, вычитания, умножения и деления. |
||
Причём, программа выводит результаты вычислений два раза |
Причём, программа выводит результаты вычислений два раза — сначала |
||
в обычном виде, а потом со специальным форматированием. |
в обычном виде, а потом со специальным форматированием. |
||
Формат <code>"%10.3lf"</code> соответствует выводу числа типа <code>double</code>, |
Формат <code>"%10.3lf"</code> соответствует выводу числа типа <code>double</code>, |
||
Строка 128: | Строка 121: | ||
Заметьте, что после каждой команды стоит точка с запятой. |
Заметьте, что после каждой команды стоит точка с запятой. |
||
Одна из самых популярных синтаксических ошибок начинающих программистов |
Одна из самых популярных синтаксических ошибок начинающих программистов — это не ставить точку c запятой в конце команды. |
||
==Примечания== |
==Примечания== |
||
{{Примечания}} |
|||
# {{note|char}} Переменные типа <code>char</code>, <code>short</code>, <code>int</code> можно печатать, используя формат "%d". Также этот формат можно использовать для печати значений [[Язык Си в примерах/Указатели в языке Си|указателей]] (номера ячейки памяти). |
|||
[[Категория:Язык Си в примерах|{{SUBPAGENAME}}]] |
[[Категория:Язык Си в примерах|{{SUBPAGENAME}}]] |
Версия от 23:59, 29 января 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;
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 запятой в конце команды.
Примечания
- ↑ 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