Брайан Керниган - Язык программирования Си. Издание 3-е, исправленное
- Название:Язык программирования Си. Издание 3-е, исправленное
- Автор:
- Жанр:
- Издательство:Невский Диалект
- Год:2001
- Город:Санкт-Петербург
- ISBN:0-13-110362-8
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Брайан Керниган - Язык программирования Си. Издание 3-е, исправленное краткое содержание
Книга широко известных авторов, разработчиков языка Си, переработанная и дополненная с учетом стандарта ANSI для языка Си, 2-е английское издание которой вышло в 1988 году, давно стала классикой для всех изучающих и/или использующих как Си, так и Си++. Русский перевод этой книги впервые был выпущен изд- вом "Финансы и статистика" в 1992 г. и с тех пор пользуется неизменным спросом читателей.
Для настоящего третьего русского издания перевод заново сверен с оригиналом, в него внесены некоторые поправки, учитывающие устоявшиеся за прошедшие годы изменения в терминологии, а так же учтены замечания, размещенные автором на странице http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html.
Для программистов, преподавателей и студентов.
Издание подготовлено при участии издательства "Финансы и статистика"
Язык программирования Си. Издание 3-е, исправленное - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Условное выражение часто позволяет сократить программу. В качестве примера приведем цикл, обеспечивающий печать 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 %d\n", ++n, power(2, n)); /* НЕВЕРНО*/
может давать несовпадающие результаты. Результат вызова функции зависит от того, когда компилятор сгенерирует команды увеличения n - до или после обращения к power. Чтобы обезопасить себя от возможного побочного эффекта, достаточно написать
++n;
printf("%d %d\n", n, power(2, n));
Обращения к функциям, вложенные присвоения, инкрементные и декрементные операторы дают "побочный эффект", проявляющийся в том, что при вычислении выражения значения некоторых переменных изменяются. В любом выражении с побочным эффектом может быть скрыта трудно просматриваемая зависимость результата выражения от очередности изменения значений переменных, входящих в выражение. В такой, например, типично неприятной ситуации
a[i] = i++; /* I.B.: doubtful example */
возникает вопрос: массив a индексируется старым или измененным значением i? Компиляторы могут по-разному генерировать программу, что проявится в интерпретации данной записи. Стандарт сознательно устроен так, что большинство подобных вопросов оставлено на усмотрение компиляторов, так как лучший порядок вычислений определяется архитектурой машины. Стандартом только гарантируется, что все побочные эффекты при вычислении аргументов проявятся перед входом в функцию. Правда, в примере с printf это нам не поможет.
Мораль такова: писать программы, зависящие от очередности вычислений, - плохая практика, какой бы язык вы ни использовали. Естественно, надо знать, чего следует избегать, но если вы не знаете, как образуются побочные эффекты на разных машинах, то лучше и не рассчитывать выиграть на особенностях частной реализации.
Глава 3. Управление
Порядок, в котором выполняются вычисления, определяется инструкциями управления. Мы уже встречались с наиболее распространенными управляющими конструкциями такого рода в предыдущих примерах; здесь мы завершим их список и более точно определим рассмотренные ранее.
3.1 Инструкции и блоки
Выражение, скажем x = 0 , или i++ , или printf(:) , становится инструкцией , если в конце его поставить точку с запятой, например:
x = 0;
i++;
printf(…);
В Си точка с запятой является заключающим символом инструкции, а не разделителем, как в языке Паскаль.
Фигурные скобки {и }используются для объединения объявлений и инструкций в составную инструкцию , или блок , чтобы с точки зрения синтаксиса эта новая конструкция воспринималась как одна инструкция. Фигурные скобки, обрамляющие группу инструкций, образующих тело функции, - это один пример; второй пример - это скобки, объединяющие инструкции, помещенные после if, else, whileили for. (Переменные могут быть объявлены внутри любого блока, об этом разговор пойдет в главе 4.) После правой закрывающей фигурной скобки в конце блока точка с запятой не ставится.
3.2 Конструкция if-else
Инструкция if-elseиспользуется для принятия решения. Формально ее синтаксисом является:
if ( выражение )
инструкция 1
else
инструкция 2
причем else-часть может и отсутствовать. Сначала вычисляется выражение, и, если оно истинно (т. е. отлично от нуля), выполняется инструкция 1 . Если выражение ложно (т. е. его значение равно нулю) и существует else-часть, то выполняется инструкция 2 .
Читать дальшеИнтервал:
Закладка: