книги / Практикум по программированию на языке Си
..pdfPRINT_EXPI(100000*100000); return 0;
}
Предупреждения при трансляции:
04_09.c: In function `main':
04_09.c:8: warning: integer overflow in expression
Результат выполнения программы:
The value of 100*(70/100) is 0
The value of 100000*100000 is 1410065408
Комментарии излишни!
ЗАДАЧА 04-10. Предположив, что переменные z, d, x, b целочисленные и что z равно -3, x равно 6, b равно 5, d равно 3, выведите (напечатайте) значения алгебраических выражений:
z |
|
, |
x − |
d |
, |
z + b + x |
. |
z + |
|
x + |
d |
3 |
|||
1 |
|
|
/* 04_10.c – скобки в арифметических выражениях */ #include <stdio.h>
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
int z=-3, x=6, d=3, b=5; PRINT_EXPI(z/(z+1)); PRINT_EXPI((x-d)/(x+d)); PRINT_EXPI((z+b+x)/3); return 0;
}
Результат выполнения программы:
The value of z/(z+1) is 1
The value of (x-d)/(x+d) is 0
The value of (z+b+x)/3 is 2
101
Обратите внимание на необходимость скобок и на округление результатов при делении.
ЗАДАЧА 04-11. Чтобы обратить внимание на ранг операции "запятая", выведите значение выражения ++z, x-=2, z+b+x и входящих в него целочисленных переменных с начальными значения-
ми x=6, b=5, z=-3.
/* 04_11.c - операция "запятая" */ #include <stdio.h>
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
int x=6, b=5, z=-3; PRINT_EXPI((++z,x-=2,z+b+x)); PRINT_EXPI(z);
PRINT_EXPI(b);
PRINT_EXPI(x); return 0;
}
Результат выполнения программы:
The value of (++z,x-=2,z+b+x) is 7
The value of z is -2
The value of b is 5
The value of x is 4
Значения операндов, связанных операцией "запятая", определяются слева направо. Значением выражения с операцией "запятая" служит значение самого правого операнда, что подтвердили результаты. Обратите внимание на необходимость скобок при использовании выражения с запятыми в качестве аргумента макроса PRINT_EXPI(). Если скобки в аргументе опустить, то компилятор воспринимает каждое из выражений, разделенных запятыми, как отдельный аргумент и выдает сообщение об ошибке при обращении к макросу:
…macro 'PRINT_EXPI' used with too many (3) args
102
4.4. Отношения и логические операции
Классическая (традиционная) версия языка Си предусматривает использование целого числа 0 для обозначения значения ЛОЖЬ. Число, отличное от нуля (чаще всего 1), трактуется как значение ИСТИНА.
ЗАДАЧА 04-12. Выведите значения отношений 5<2, 4+1>4, 6!=5, 2·2==4.
/* 04_12.c - значения отношений */ #include <stdio.h>
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
PRINT_EXPI(5 < 2); PRINT_EXPI(4+1 > 4); PRINT_EXPI(6 != 5); PRINT_EXPI(2*2 == 4); return 0;
}
Результат выполнения программы:
The value of 5 < 2 is 0
The value of 4+1 > 4 is 1
The value of 6 != 5 is 1
The value of 2*2 == 4 is 1
Операции отношений и логические операции имеют разные ранги, что нужно учитывать при составлении логических выражений.
ЗАДАЧА 04-13. Оцените, какие значения будут иметь выражения:
!2, !6<4, !(6<4), 2·2==!4.
/* 04_13.c - выражения с операцией ! */ #include <stdio.h>
103
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
PRINT_EXPI(!2); PRINT_EXPI(!6<4); PRINT_EXPI(!(6<4)); PRINT_EXPI(2*2 == !4); return 0;
}
Результат выполнения программы:
The value of !2 is 0
The value of !6<4 is 1
The value of !(6<4) is 1
The value of 2*2 == !4 is 0
В логическом выражении с несколькими операциями одного ранга вычисление завершается, как только будет очевиден результат. Справедливость сказанного можно проиллюстрировать следующей задачей.
ЗАДАЧА 04-14. Напишите программу, иллюстрирующую на примере требований к длинам сторон треугольника справедливость утверждения о том, что логическое выражение с несколькими конъюнкциями вычисляется до тех пор, пока истинны очередные логические сомножители.
Логическое выражение, которому должны удовлетворять длины a, b, c сторон треугольника, в конъюнктивной форме имеет вид:
a+b>c && a+c>b && b+c>a
При ложности любого из трех отношений, входящих в приведенное неравенство, остальные (стоящие справа) не вычисляются – длины сторон выбраны неверно.
Возможны разные подходы к решению задачи. Например, можно в каждом из трех отношений (логических сомножителей) изменять
104
значение одной переменной с помощью постфиксной унарной операции (++ или --), а в следующем (слева направо) отношении восстанавливать это значение префиксной симметричной унарной операцией (-- или ++). Соответствующие выражения:
a--+b>c |
(изменили а), |
|
изменили |
b), |
++a+c>b-- |
(восстановили a, |
|||
++b+c>--a |
(восстановили |
b, |
изменили |
с). |
Выведя значения a, b, c до вычисления логического выражения или после вычисления легко установить, какие отношения вычислялись. Следующая программа реализует указанный подход:
/* 04_14.c - последовательность вычисления логических выражений */
#include <stdio.h>
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
int a=3, b=7, c=2;
PRINT_EXPI(a-- +b>c && ++a+c>b-- && ++b+c-->a); PRINT_EXPI(a);
PRINT_EXPI(b);
PRINT_EXPI(c); return 0;
}
Результат выполнения программы:
The value of a-- +b>c && ++a+c>b-- && ++b+c-->a is 0 The value of a is 3
The value of b is 6 The value of c is 2
При выбранных значениях a=3, b=7, c=2 второй из логических сомножителей ложен (равен 0), тем самым значение конъюнкции определено (ложь), изменено значение переменной b, значение a "восстановлено", значение переменной c не менялось – третий сомножитель не вычислялся.
105
ЗАДАЧА 04-15. Напишите программу, иллюстрирующую на примере требований к длинам сторон треугольника справедливость утверждения о том, что логическое выражение с несколькими дизъюнкциями вычисляется до тех пор, пока ложны очередные логические слагаемые.
Логическое выражение для проверки неправильности выбора длин a, b, c сторон треугольника можно записать в дизъюнктивной форме:
a+b<=c || a+c<=b || b+c<=a
Треугольник со сторонами a, b, c нельзя построить, если приведенное логическое выражение истинно.
Вычисление приведенного дизъюнктивного выражения выполняется слева направо до тех пор, пока значения входящих в него отношений остаются ложными. При истинности любого из отношений остальные (стоящие справа) не вычисляются.
Введем три флажка – целочисленные переменные f1, f2, f3, равные 0 до вычисления логического выражения. Каждое из логических "слагаемых" представим в виде выражения с операцией "запятая" и заключим в скобки:
(++f1, a+b<=c) || (++f2, a+c<=b) || (++f3, b+c<=a)
Скобки необходимы, так как ранг операции "запятая" самый низкий. Вычислив дизъюнктивное логическое выражение, напечатаем значения флажков, что выполнено в следующей программе:
/* 04_15.c - дизъюнкция выражений */ #include <stdio.h>
#define PRINT_EXPI(EXPRESSION)\
printf("The value of "#EXPRESSION" is %d\n", EXPRESSION)
int main ()
{
int a=3, b=8, c=5; int f1=0, f2=0, f3=0; PRINT_EXPI
((++f1,a+b<=c)||(++f2,a+c<=b)||(++f3,b+c<=a)); PRINT_EXPI(f1);
106
PRINT_EXPI(f2); PRINT_EXPI(f3); return 0;
}
Результат выполнения программы:
The value of (++f1,a+b<=c)||(++f2,a+c<=b)||(++f3,b+c<=a) is 1 The value of f1 is 1
The value of f2 is 1 The value of f3 is 0
Флажок f3 остался без изменений – при использованных значениях a, b, c истинность логического выражения установлена при проверке второго отношения: a+c<=b.
4.5. Условная (тернарная) операция
ЗАДАЧА 04-16. Заданы радиус круга (r) и длина стороны квадрата (d). Если квадрат можно вписать в круг, вычислите площадь круга. Если квадрат нельзя вписать в круг, вычислите площадь квадрата. После вычисления площади уменьшите на 1 соответствующий линейный размер, т.е. если вычислялась площадь круга, – уменьшите r, если площадь квадрата, – уменьшите d. Используйте условную операцию, чтобы записать решение с помощью одного выражения.
/* 04_16.c - условная (тернарная) операция */ #include <stdio.h>
#define PRINTF(EXPRESSION) \ printf(#EXPRESSION"=%f\n",EXPRESSION)
int main ()
{
float r=3, d=4;
PRINTF((d/2)*(d/2)*2 < r*r ? 3.1415*r*r-- : d*d--); PRINTF(r);
PRINTF(d);
PRINTF((d/2)*(d/2)*2 < r*r ? 3.1415*r*r-- : d*d--);
107
PRINTF(r);
PRINTF(d); return 0;
}
Результат выполнения программы:
(d/2)*(d/2)*2 < r*r ? 3.1415*r*r-- : d*d--=28.273500 r=2.000000
d=4.000000
(d/2)*(d/2)*2 < r*r ? 3.1415*r*r-- : d*d--=16.000000 r=2.000000
d=3.000000
В выражениях – операндах условной операции использована унарная операция декремента, уменьшающая значение либо r, либо d в зависимости от того, какое из выражений вычислено. По выведенным значениям проследите за порядком вычислений. Вначале вычислена площадь круга и изменилось (уменьшилось на 1) значение r. При втором выполнении вычислена площадь квадрата и уменьшено значение d.
ЗАДАЧА 04-17. Заданы точки x1, x2, x3 на числовой оси, определяющие четыре промежутка:
x!x1 |
(полуоткрытый промежуток 1); |
x1<x!x2 |
(полуоткрытый промежуток 2); |
x2<x!x3 |
(полуоткрытый промежуток 3); |
x3<x |
(открытый промежуток 4). |
Используя тернарную (условную) операцию, записать выражение для вычисления номера промежутка, которому принадлежит заданное значение.
Задачу для некоторых фиксированных x1, x2, x3 и значений переменных x, y, z, e решает следующая программа:
/* 04_17.c - "вложение" условных операций */ #include <stdio.h>
#define PRINTI(EXPRESSION) \
printf(#EXPRESSION" equals %d\n",EXPRESSION) int main ()
108
{
float x1=16.3, x2=19.0, x3=129.6;
float x=10.0, y=18.5, z=100.2, e=200.0;
PRINTI( x<=x1 ? 1 : x<=x2 ? 2 : x<=x3 ? 3 : 4); PRINTI( y<=x1 ? 1 : y<=x2 ? 2 : y<=x3 ? 3 : 4); PRINTI( z>x1 ? z>x2 ? z>x3 ? 4 : 3 : 2 : 1); PRINTI( e>x1 ? e>x2 ? e>x3 ? 4 : 3 : 2 : 1); return 0;
}
Результат выполнения программы:
x<=x1 ? 1 : x<=x2 ? 2 : x<=x3 ? 3 : 4 equals 1 y<=x1 ? 1 : y<=x2 ? 2 : y<=x3 ? 3 : 4 equals 2 z>x1 ? z>x2 ? z>x3 ? 4 : 3 : 2 : 1 equals 3 e>x1 ? e>x2 ? e>x3 ? 4 : 3 : 2 : 1 equals 4
Впрограмме использованы две разные формы выражений с вложением условных операций. Напомним формат выражения с тернарной операцией:
выражение_1?выражение_2:выражение_3
Впервых двух формах (для x и y) выражение с условной опера-
цией используется (подставляется) на место выражения_3. Во вторых двух формах (для z и e) выражение с условной операцией используется (подставляется) на место выражения_2.
Запись выражений с вложением условных операций может вызвать у начинающих затруднения. Проиллюстрируем последовательность проверок условий в таких выражениях.
ЗАДАЧА 04-18. В выражения-операнды 1, 2, 3 условной операции ввести "флажки", позволяющие проследить за последовательностью вычислений. Определить конкретные выражения с вложением условных операций и, выведя значения флажков, показать порядок вычислений.
Еще раз обратимся к оценке попадания арифметического значения в интервал на числовой оси из задачи 04-17. Используем две формы выражений с вложением условных операций. В качестве
109
флажков применим целочисленные переменные r1, r2, r3. Включим в каждое проверяемое условие присваивание флажку некоторого значения. Условие и присваивание флажку значения объединим с помощью операции "запятая" и заключим в скобки. Получим для задачи "об интервалах" такие условия:
(r1=1, y<=x1), (r2=1, y<=x2), (r3=1, y<=x3).
Программная реализация:
/* 04_18.c - последовательность вычисления условных выражений */
#include <stdio.h>
#define PRINTI(EXPRESSION) \
printf(#EXPRESSION" equals %d\n",EXPRESSION) int main ()
{
float x1=16.3, x2=19.0, x3=129.6; float y=18.5;
int r1=0, r2=0, r3=0; PRINTI
((r1=1,y<=x1)?1:(r2=1,y<=x2)?2:(r3=1,y<=x3)?3:4 ); PRINTI(r1);
PRINTI(r2);
PRINTI(r3);
r1=r2=r3=0; PRINTI
((r1=1,y>x1)?(r2=1,y>x2)?(r3=1,y>x3)?4:3:2:1);
PRINTI(r1);
PRINTI(r2);
PRINTI(r3); return 0;
}
Результат выполнения программы:
(r1=1,y<=x1)?1:(r2=1,y<=x2)?2:(r3=1,y<=x3)?3:4 equals 2
r1 equels 1
110