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

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Замечена ошибка в коде. Копировал код в компилятор - не хотел компилировать. Поставил запятую - все заработало как надо. Компилятор: Dev C++.
Использованы <var />, <source enclose="none" />, <em /> где возможно; →‎Примечания: перенесены <ref /> в данный раздел; использован {{Cite web|}}.
Строка 1: Строка 1:
{{{{BASEPAGENAME}}/Содержание}}
{{{{Book template}}/Содержание}}


Разнообразные вычисления —
Разнообразные вычисления —
Строка 29: Строка 29:
</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 >printf</code>.<ref name="fprintf" /> Данная функция выведет целое число в десятичной форме (согласно ''указателю преобразования'' <code >%d</code>), завершая вывод [[w:Перевод строки |переводом строки]] (<code >\n</code>).


Число, которое будет выведено, является результатом вычисления выражения <code>a + b</code> — или же, проще говоря, — суммой значений переменных <code>a</code> и <code>b</code>.
Число, которое будет выведено, является результатом вычисления выражения <source lang="c" enclose="none" >a + b</source> — или же, проще говоря, — суммой значений переменных <var >a</var> и <var >b</var>.


Вводимые пользователем числа помещаются в эти переменные функцией <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 >scanf</code>, вызываемой в коде выше как <source lang="c" enclose="none" >scanf ("%d%d", &a, &b)</source>.<ref name="fscanf" /> Здесь, как и в <code >printf</code>, используется указатель преобразования <code >%d</code>, означающий на этот раз ''считывание'' числа в десятичной форме (возможно — предваряемого пробелами). Поскольку указатель повторен дважды, два числа будут считаны и помещены в упомянутые в аргументах две переменные <var >a</var> и <var >b</var>. (Необходимый здесь унарный оператор <code >&</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 >printf</code>, функция <code >scanf</code> ''объявлена'' в ''заголовке'' (англ. {{lang |en|header}}) <code >stdio.h</code>.<ref name="stdio.h" />


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


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


<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>
<small >Действие ''макроподстановки'' <code >assert</code> заключается в ''вычислении'' выражения, переданного ей первым (и единственным) аргументом и ''аварийном завершении'' программы в случае, если полученное значение — ноль («логическая ложь».)<ref name="assert" /></small>


Наконец, в самом начале функции <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 >main</code> ''определены'' (помимо упомянутой уже <var >r</var>) целочисленные переменные <var >a</var> и <var >b</var>. Их значение в начале выполнения функции <code >main</code> может быть произвольным,<ref name="init" /> но после успешного (что проверяется использованием <code >assert</code>) завершения <code >scanf</code> они будут содержать два числа, которые удалось последовательно считать со [[w:Стандартные потоки#Стандартный ввод |стандартного ввода]].


== Вариант «дробный» ==
== Вариант «дробный» ==
Строка 65: Строка 65:
</source>
</source>


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


<small>Здесь следует отметить, что в случае <code>scanf</code> совершенно идентично будут действовать указатели преобразований <code>%lg</code>, <code>%lf</code> и <code>%le</code>. Напротив, в случае <code>printf</code> не будет разницы между <code>%lg</code> и <code>%g</code>. Причины такого поведения мы также пока оставим без внимания.</small>
<small>Здесь следует отметить, что в случае <code>scanf</code> совершенно идентично будут действовать указатели преобразований <code>%lg</code>, <code>%lf</code> и <code>%le</code>. Напротив, в случае <code>printf</code> не будет разницы между <code>%lg</code> и <code>%g</code>. Причины такого поведения мы также пока оставим без внимания.</small>
Строка 79: Строка 79:
Так, запись <code>&a</code> означает ''адрес ячейки памяти, выделенной для хранения значения переменной <code>a</code>'', или же, проще, — ''ссылка'' (англ. {{lang|en|reference}}) на переменную <code>a</code>.
Так, запись <code>&a</code> означает ''адрес ячейки памяти, выделенной для хранения значения переменной <code>a</code>'', или же, проще, — ''ссылка'' (англ. {{lang|en|reference}}) на переменную <code>a</code>.


Справа от оператора <code>&</code> указывается ''л-значение'' (англ. {{lang|en|lvalue}}.<ref name="lvalues et al" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=72 WG14 N1570 Committee Draft — April 12, 2011] 6.3.2.1 ''Lvalues, arrays, and function designators''</ref> Не вдаваясь в подробности отметим, что под л-значением понимают любое выражение, которое может стоять ''слева'' от оператора присваивания <code>=</code>. В частности, л-значением является любое выражение, состоящие из имени действительной в данном контексте (англ. {{lang|en|scope}}) переменной.
Справа от оператора <code >&</code> указывается ''л-значение'' (англ. {{lang|en|lvalue}}.<ref name="lvalues et al" /> Не вдаваясь в подробности отметим, что под л-значением понимают любое выражение, которое может стоять ''слева'' от оператора присваивания <code >=</code>. В частности, л-значением является любое выражение, состоящие из имени действительной в данном контексте (англ. {{lang |en|scope}}) переменной.


Для обращения к ячейки памяти по ссылке используется унарный оператор <code>*</code>. Так, выражение <code>*(&a) = 1</code> полностью равнозначно <code>a = 1</code>.
Для обращения к ячейки памяти по ссылке используется унарный оператор <code >*</code>. Так, выражение <source lang="c" enclose="none" >*(&a) = 1</source> полностью равнозначно <source lang="c" enclose="none" >a = 1</source>.


== Вариант «арифметический» ==
== Вариант «арифметический» ==
Строка 114: Строка 114:
</source>
</source>


В этой программе нам потребовался ''условный оператор контекста утверждения'' (англ. {{lang|en|if statement}}) — один из четырех (наряду с <code>&&</code>, <code>||</code> и <code>? :</code>) условных операторов языка. Его синтаксис:<ref name="if" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=166 WG14 N1570 Committee Draft — April 12, 2011] 6.8.4.1 ''The if statement''</ref>
В этой программе нам потребовался ''условный оператор контекста утверждения'' (англ. {{lang |en|if statement}}) — один из четырех (наряду с <code >&&</code>, <code >||</code> и <code >? :</code>) условных операторов языка. Его синтаксис:<ref name="if" />
<code>if (</code>⟨''выражение''⟩<code>)</code>
<code >if (<var class="angled" >выражение</var>)
⟨''тело-если-истинно''⟩
<var class="angled" >тело-если-истинно</var></code>
<code>if (</code>⟨''выражение''⟩<code>)</code>
<code >if (<var class="angled" >выражение</var>)
⟨''тело-если-истинно''⟩
<var class="angled" >тело-если-истинно</var>
<code>else</code>
else
⟨''тело-если-ложно''⟩
<var class="angled" >тело-если-ложно</var></code>


Где ⟨''тело-если-истинно''⟩ и ⟨''тело-если-ложно''⟩ могут быть (каждый) единственным утверждением (завершаемым <code>;</code>), или же, как в примере выше, — <code>{ }</code>-блоком.
Где <var class="angled" >тело-если-истинно</var> и <var class="angled" >тело-если-ложно</var> могут быть (каждый) единственным утверждением (завершаемым <code >;</code>), или же, как в примере выше, — <code >{ }</code>-блоком.


В случае, если результат вычисления ''выражения'' — истина (другими словами — отличен от 0), выполняется ⟨''тело-если-истинно''⟩; в противном случае (и если используется <code>else</code>) — ⟨''тело-если-ложно''⟩.
В случае, если результат вычисления <var >выражения</var> — истина (другими словами — отличен от 0), выполняется <var class="angled" >тело-если-истинно</var>; в противном случае (и если используется <code >else</code>) — <var class="angled" >тело-если-ложно</var>.


Заметьте, что каждое простое ''утверждение'' (англ. {{lang|en|statement}}) завершается точкой с запятой.
Заметьте, что каждое простое ''утверждение'' (англ. {{lang|en|statement}}) завершается точкой с запятой.
Строка 160: Строка 160:
В этом варианте, код вновь содержит те же самые строковые константы, что и в предыдущем, однако используется лишь один вызов функции <code>printf</code> — первым аргументом которой (в зависимости от значения переменной <code>b</code>) окажется одна из этих констант.
В этом варианте, код вновь содержит те же самые строковые константы, что и в предыдущем, однако используется лишь один вызов функции <code>printf</code> — первым аргументом которой (в зависимости от значения переменной <code>b</code>) окажется одна из этих констант.


Число 42, которое передается <code>printf</code> пятым аргументом в случае нулевого значения <code>b</code>, может быть произвольным. Поскольку в этом случае функция <code>printf</code> получает строку формата лишь с ''тремя'' указателями формата, фактически использованы будут лишь аргументы с первого (формат) по четвертый (произведение <code>a</code> и <code>b</code>).
Число 42, которое передается <code >printf</code> пятым аргументом в случае нулевого значения <var >b</var>, может быть произвольным. Поскольку в этом случае функция <code >printf</code> получает строку формата лишь с <em >тремя</em> указателями формата, фактически использованы будут лишь аргументы с первого (формат) по четвертый (произведение <var >a</var> и <var >b</var>).


Общий синтаксис тернарного оператора <code>? :</code> следующий:<ref name="conditional" >[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=118 WG14 N1570 Committee Draft — April 12, 2011] 6.5.15 ''Conditional operator''</ref>
Общий синтаксис тернарного оператора <code >? :</code> следующий:<ref name="conditional" />
''выражение'' <code>?</code> ''если-истинно'' <code>:</code> ''если-ложно''
<code ><var class="angled" >выражение</var> ? <var class="angled" >если-истинно</var> : <var class="angled" >если-ложно</var></code>


Где ''выражение'' вычисляется первым, после чего, если оно истинно (отлично от 0), вычисляется и становится результатом оператора в целом выражение ''если-истинно''; в противном случае — ''если-ложно''.
Где <var class="angled" >выражение</var> вычисляется первым, после чего, если оно истинно (отлично от 0), вычисляется и принимается результатом оператора в целом выражение <var class="angled" >если-истинно</var>; в противном случае — <var class="angled" >если-ложно</var>.


==Примечания==
== Примечания ==
{{Примечания | refs =

<!-- Пожалуйста поддерживайте алфавитный порядок для name. Спасибо. -->
{{Примечания}}
<ref name="assert" >{{Cite web | title = 7.2.1.1 The assert macro | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=204 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="conditional" >{{Cite web | title = 6.5.15 Conditional operator | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=118 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="fprintf" >{{Cite web | title = 7.21.6.1 The fprintf function | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=327 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="fscanf" >{{Cite web | title = 7.21.6.2 The fscanf function | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=335 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="if" >{{Cite web | title = 6.8.4.1 The if statement | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=166 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="init" >{{Cite web | title = 6.7.9 Initialization | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=157 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="lvalues et al" >{{Cite web | title = 6.3.2.1 Lvalues, arrays, and function designators | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=72 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="stdio.h" >{{Cite web | title = 7.21 Input/output <code >stdio.h</code> | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=314 | work = WG14 N1570 Committee Draft | publisher = <!-- FIXME: ? --> | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
}}


{{BookCat}}
{{BookCat}}

Версия от 14:56, 5 марта 2015

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


  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 совершенно идентично будут действовать указатели преобразований %lg, %lf и %le. Напротив, в случае printf не будет разницы между %lg и %g. Причины такого поведения мы также пока оставим без внимания.

Желающим изучить использование других числовых типов в этой задаче предлагается обратиться к разделу «Числовые типы» приложения.

Л-значения и ссылки

Вызов функции в чем-то подобен отправке письма. Мы указываем на конверте имя функции, вкладываем в конверт копии значений, которые хотим передать, и отправляем письмо (вызываем функцию).

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

Так, запись &a означает адрес ячейки памяти, выделенной для хранения значения переменной a, или же, проще, — ссылка (англ. reference) на переменную a.

Справа от оператора & указывается л-значение (англ. lvalue.[6] Не вдаваясь в подробности отметим, что под л-значением понимают любое выражение, которое может стоять слева от оператора присваивания =. В частности, л-значением является любое выражение, состоящие из имени действительной в данном контексте (англ. scope) переменной.

Для обращения к ячейки памяти по ссылке используется унарный оператор *. Так, выражение *(&a) = 1 полностью равнозначно a = 1.

Вариант «арифметический»

В программе ниже мы кроме суммы вычислим разность и произведение введенных чисел, а также, если второе число отлично от нуля, — частное от деления первого числа на второе.

#include <assert.h>
#include <stdio.h>

int
main ()
{
  double a, b;
  int r
    = scanf ("%lg%lg", &a, &b);
  assert (r == 2);
  if (b != 0) {
    printf (("a + b = %lg\n"
             "a - b = %lg\n"
             "a * b = %lg\n"
             "a / b = %lg\n"),
            a + b, a - b, a * b, a / b);
  } else {
    printf (("a + b = %lg\n"
             "a - b = %lg\n"
             "a * b = %lg\n"),
            a + b, a - b, a * b);
  }
  return 0;
}

В этой программе нам потребовался условный оператор контекста утверждения (англ. if statement) — один из четырех (наряду с &&, || и ? :) условных операторов языка. Его синтаксис:[7]

if (выражение)
  тело-если-истинно

if (выражение)
  тело-если-истинно
else
  тело-если-ложно

Где тело-если-истинно и тело-если-ложно могут быть (каждый) единственным утверждением (завершаемым ;), или же, как в примере выше, — { }-блоком.

В случае, если результат вычисления выражения — истина (другими словами — отличен от 0), выполняется тело-если-истинно; в противном случае (и если используется else) — тело-если-ложно.

Заметьте, что каждое простое утверждение (англ. statement) завершается точкой с запятой. Одна из самых популярных синтаксических ошибок начинающих программистов — не ставить точку c запятой после утверждений.

Вариант «тернарный»

Используя условный оператор контекста выражения (англ. conditional operator), а также приняв во внимание тот факт, что printf проигнорирует «избыточные» аргументы (аргументы сверх количества, требуемого указателями преобразований в указанной первым аргументом строке формата), можно незначительно сократить код предыдущего примера.

#include <assert.h>
#include <stdio.h>

int
main ()
{
  double a, b;
  int r = scanf ("%lg%lg", &a, &b);
  assert (r == 2);
  printf ((b != 0
           ? ("a + b = %lg\n"
              "a - b = %lg\n"
              "a * b = %lg\n"
              "a / b = %lg\n")
	   : ("a + b = %lg\n"
              "a - b = %lg\n"
              "a * b = %lg\n")),
          a + b, a - b, a * b,
	  (b != 0 ? a / b : 42));
  return 0;
}

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

Число 42, которое передается printf пятым аргументом в случае нулевого значения b, может быть произвольным. Поскольку в этом случае функция printf получает строку формата лишь с тремя указателями формата, фактически использованы будут лишь аргументы с первого (формат) по четвертый (произведение a и b).

Общий синтаксис тернарного оператора ? : следующий:[8]

выражение ? если-истинно : если-ложно

Где выражение вычисляется первым, после чего, если оно истинно (отлично от 0), вычисляется — и принимается результатом оператора в целом — выражение если-истинно; в противном случае — если-ложно.

Примечания

  1. 7.21.6.1 The fprintf function(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  2. 7.21.6.2 The fscanf function(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  3. 7.21 Input/output stdio.h(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  4. 7.2.1.1 The assert macro(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  5. 6.7.9 Initialization(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  6. 6.3.2.1 Lvalues, arrays, and function designators(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  7. 6.8.4.1 The if statement(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.
  8. 6.5.15 Conditional operator(англ.) WG14 N1570 Committee Draft Проверено 2012-11-19 г.