Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Практикум по программированию на языке Си

..pdf
Скачиваний:
23
Добавлен:
12.11.2023
Размер:
3.53 Mб
Скачать

PRINT_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