Категории
Самые читаемые
ChitatKnigi.com » 🟢Компьютеры и Интернет » Программирование » Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган

Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган

Читать онлайн Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган
1 ... 12 13 14 15 16 17 18 19 20 ... 81
Перейти на страницу:

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать

‹‹

››

&

^

|

Если выр1 и выр2 - выражения, то

выр1 op= выр2

Эквивалентно

выр1 = (выр1) op (выр2)

с той лишь разницей, что выр1 вычисляется только один раз. Обратите внимание на скобки вокруг выр2:

x *= y + 1

эквивалентно

x = x * (y + 1)

но не

x=x*y+1

В качестве примера приведем функцию bitcount, подсчитывающую число единичных битов в своем аргументе целочисленного типа.

/* bitcount: подсчет единиц в х */

int bitcount(unsigned х)

{

 int b;

 for (b = 0; х != 0; x ››= 1)

  if (x & 01)

   b++;

 return b;

}

Независимо от машины, на которой будет работать эта программа, объявление аргумента x как unsigned гарантирует, что при правом сдвиге освобождающиеся биты будут заполняться нулями, а не знаковым битом.

Помимо краткости операторы присваивания обладают тем преимуществом, что они более соответствуют тому, как человек мыслит. Мы говорим "прибавить 2 к i" или "увеличить i на 2", а не "взять i, добавить 2 и затем вернуть результат в i", так что выражение i+=2 лучше, чем i=i+2. Кроме того, в сложных выражениях вроде

yyval[yypv[p3+p4] + yypv[p1+p2]]+= 2

благодаря оператору присваивания += запись становится более легкой для понимания, так как читателю при такой записи не потребуется старательно сравнивать два длинных выражения, совпадают ли они, или выяснять, почему они не совпадают. Следует иметь в виду и то, что подобные операторы присваивания могут помочь компилятору сгенерировать более эффективный код.

Мы уже видели, что присваивание вырабатывает значение и может применяться внутри выражения: вот самый расхожий пример:

while ((с = getchar()) != EOF)

В выражениях встречаются и другие операторы присваивания (+=, -= и т. д.), хотя и реже. Типом и значением любого выражения присваивания являются тип и значение его левого операнда после завершения присваивания.

Упражнение 2.9. Применительно к числам, в представлении которых использован дополнительный код, выражение x &= (x-1) уничтожает самую правую 1 в x. Объясните, почему. Используйте это наблюдение при написании более быстрого варианта функции bitcount.

2.11 Условные выражения

Инструкции

if (a › b)

 z = a;

else

 z = b;

пересылают в z большее из двух значений a и b. Условное выражение, написанное с помощью тернарного (т. е. имеющего три операнда) оператора "?: ", представляет собой другой способ записи этой и подобных ей конструкций. В выражении

выр1 ? выр2 : выр3

первым вычисляется выражение выр1. Если его значение не нуль (истина), то вычисляется выражение выр2, и значение этого выражения становится значением всего условного выражения. В противном случае вычисляется выражение выр3 и его значение становится значением условного выражения. Следует отметить, что из выражений выр2 и выр3 вычисляется только одно из них. Таким образом, чтобы установить в z большее из a и b, можно написать

z = (a › b) ? a: b; /* z = max(a, b) */

Следует заметить, что условное выражение и в самом деле является выражением, и его можно использовать в любом месте, где допускается выражение. Если выр2 и выр3 принадлежат разным типам, то тип результата определяется правилами преобразования, о которых шла речь в этой главе ранее. Например, если f имеет тип float, а n - тип int, то типом выражения

(n › 0) ? f : n

будет float вне зависимости от того, положительно значение n или нет.

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

Условное выражение часто позволяет сократить программу. В качестве примера приведем цикл, обеспечивающий печать n элементов массива по 10 на каждой строке с одним пробелом между колонками; каждая строка цикла, включая последнюю, заканчивается символом новой строки:

for (i = 0; i ‹ n; i++)

 printf("%6d %c", a[i], (i%10 == 9 || i == n-1) ? 'n' : ' ');

Символ новой строки посылается после каждого десятого и после n-го элемента. За всеми другими элементами следует пробел. Эта программа выглядит довольно замысловато, зато она более компактна, чем эквивалентная программа с использованием if-else. Вот еще один хороший пример:

printf("Вы имеете %d элемент%s: n", n, (n%10 == 1 && n%100 != 11)?

 " ": ((n%100 ‹ 10 || n%100 › 20) && n%10 ›= 2 && n%10 ‹= 4) ?

 "a": "ов");

Упражнение 2.10. Напишите функцию lower, которая переводит большие буквы в малые, используя условное выражение (а не конструкцию if-else).

2.12 Приоритет и очередность вычислений

В таблице 2.1 показаны приоритеты и очередность вычислений всех операторов, включая и те, которые мы еще не рассматривали. Операторы, перечисленные на одной строке, имеют одинаковый приоритет: строки упорядочены по убыванию приоритетов; так, например, *, / и % имеют одинаковый приоритет, который выше, чем приоритет бинарных + и -. "Оператор" () относится к вызову функции. Операторы -› и . (точка) обеспечивают доступ к элементам структур; о них пойдет речь в главе 6, там же будет рассмотрен и оператор sizeof (размер объекта). Операторы * (косвенное обращение по указателю) и & (получение адреса объекта) обсуждаются в главе 5. Оператор "запятая" будет рассмотрен в главе 3.

Таблица 2.1. Приоритеты и очередность вычислений операторов

Операторы Выполняются () [] -› . слева направо ! ~ ++ -- + - * & (type) sizeof справа налево * / % слева направо + - слева направо ‹‹ ›› слева направо ‹ ‹= › ›= слева направо ==  != слева направо &  слева направо ^ слева направо | слева направо && слева направо || слева направо ?: справа налево = += -= *= /= %= &= ^= |= ‹‹= ››= справа налево , слева направо

Примечание. Унарные операторы +, -, * и & имеют более высокий приоритет, чем те же бинарные операторы.

Заметим, что приоритеты побитовых операторов &, ^ и | ниже, чем приоритет == и !=, из-за чего в побитовых проверках, таких как

if ((x & MASK) == 0)…

чтобы получить правильный результат, приходится использовать скобки. Си подобно многим языкам не фиксирует очередность вычисления операндов оператора (за исключением &&, ||, ?: и ,). Например, в инструкции вида

x = f() + g();

f может быть вычислена раньше g или наоборот. Из этого следует, что если одна из функций изменяет значение переменной, от которой зависит другая функция, то помещаемый в x результат может зависеть от очередности вычислений. Чтобы обеспечить нужную последовательность вычислений, промежуточные результаты можно запоминать во временных переменных.

Очередность вычисления аргументов функции также не определена, поэтому на разных компиляторах

printf("%d %dn", ++n, power(2, n)); /* НЕВЕРНО*/

может давать несовпадающие результаты. Результат вызова функции зависит от того, когда компилятор сгенерирует команды увеличения n - до или после обращения к power. Чтобы обезопасить себя от возможного побочного эффекта, достаточно написать

++n;

printf("%d %dn", n, power(2, n));

Обращения к функциям, вложенные присвоения, инкрементные и декрементные операторы дают "побочный эффект", проявляющийся в том, что при вычислении выражения значения некоторых переменных изменяются. В любом выражении с побочным эффектом может быть скрыта трудно просматриваемая зависимость результата выражения от очередности изменения значений переменных, входящих в выражение. В такой, например, типично неприятной ситуации

1 ... 12 13 14 15 16 17 18 19 20 ... 81
Перейти на страницу:
Открыть боковую панель
Комментарии
Настя
Настя 08.12.2024 - 03:18
Прочла с удовольствием. Необычный сюжет с замечательной концовкой
Марина
Марина 08.12.2024 - 02:13
Не могу понять, где продолжение... Очень интересная история, хочется прочесть далее
Мприна
Мприна 08.12.2024 - 01:05
Эх, а где же продолжение?
Анна
Анна 07.12.2024 - 00:27
Какая прелестная история! Кратко, ярко, захватывающе.
Любава
Любава 25.11.2024 - 01:44
Редко встретишь большое количество эротических сцен в одной истории. Здесь достаточно 🔥 Прочла с огромным удовольствием 😈