Язык Си - Уэйт, Прата, Мартин. M. уэит с. Прата д. Мартин
Скачать 4.69 Mb.
|
while до следующего символа "точка с запятой".) Печать данных будет произведена только один раз - после завершения цикла. В фрагменте 2 наличие фигурных скобок гарантирует, что оба оператора являются частью цикла while, и печать результатов будет изводиться на каждом шаге работы цикла. Весь составной оператор рассматривается как один оператор, являющийся составной частью оператора while. 87 РИС. 5.7. Цикл while с составным оператором Давайте опять посмотрим на фрагменты, содержащие цикл while, и обратим внимание на то, как мы использовали отступы от поля в строках для выделения тела циклов while. Для компилятора отступы в строке не имеют никакого значения; решения вопроса о том, как интерпретировать наши инструкции, он использует фигурные скобки и свое "знание" правил формирования структуры цикла while. Отступы в строках предназначены для нас, чтобы с первого взгляда можно было понять, как устроена программа. Ранее мы показали вам один популярный способ расстановки фигурных скобок, служащий для указания блока или составного оператора. Другой, тоже довольно распространенный способ выглядит следующим образом while(index++ < 10) { sam = 10 * index + 2; printf(" sam = %d\n", sam); } Этот способ акцентирует внимание на том, что данные операторы образуют блок, в то время как способ, обсуждавшийся выше, указывает на принадлак ность блока оператору while. Заметим снова, что поскольку дело касается компилятора, обе формы являются абсолютно идентичными. Подводя итоги, скажем применяйте отступы от поля в строках, чтобы сделать структуру программы наглядной. Резюме: выражения и операторы Выражение состоит из операций и операндов. Примерами простейших выражений являются константы или переменные (операция отсутствует), такие, как 22 или beebop. Более сложные выражения - это 55 + 22 и vap = 2*(vip + (mgx = 4 )). Оператор служит камандой компьютеру. Операторы бывают простыми и составными. Простые операторы оканчиваются символом "точка с запятой". Примеры: 88 1. Операторы описания int toes; 2. Операторы присваивания toes = 12; 3. Операторы вызова функции printf(" %d\n", toes); 4. Управляющие операторы while (toes < 20) toes = toes + 2; 5. Пустой оператор ; Составные операторы, или блоки, состоят из одного или более операторов (ко торые в свою очередь сами могут быть составными), заключенных в фигурные скобки. Оператор while, приведенный ниже, содержит, например, составной oпeратор while(years < 100) { wisdom = wisdom + 1; printf(" %d %d\n" , years, wisdom); } ПРЕОБРАЗОВАНИЕ ТИПОВ Далее Содержание В операторах и выражениях, вообще говоря, должны использоваться переменные и константы только одного типа. Если все же вы смешаете типы в одном выражении, то компилятор с языка Си не считает программу неправильной, как это произошло бы при программировании на Паскале. Вместо этого компилятор использует набор правил для автоматического преобразования типов. Это очень удобно, но может оказаться и опасным, особенно если вы допустили смешение типов нечаянно. (Например, программа lint, работающая в операционной системе UNIX, проверяет несоответствие типов.) Нам представляется разумным привести несколько основных правил, касающихся преобразования типов: 1. Если операция выполняется над данными двух различных типов, обе величины приводятся к "высшему" из двух типов. Этот процесс называется "повышением" типа. 2. Последовательность имен типов, упорядоченных от "высшего" к "низшему", выглядит так: double, float, long, int, short и char. Применение ключевого слова unsigned повышает ранг соответствующего типа данных со знаком. 3. В операторе присваивания конечный результат вычисления выражения в правой части приводится к типу переменной, которой должно быть присвоено это значение. Данный процесс может привести к "повышению" типа, как описано выше, или к "понижению, при котором величина приводится к типу данных, имеющему более низкий приоритет. Повышение" типа обычно происходит гладко, в то время как понижение" может привести к затруднениям. Причина этого проста: все число целиком может не поместиться в элементе данных низшего типа. Переменная типа char может иметь целое значение 101, но не 22334. Пример, приведенный ниже, иллюстрирует применение этих правил. /* Преобразования */ main() { char ch; int i; float fl; fl = i = ch = 'А'; /* строка 8 */ printf(" ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); ch = ch + 1; /* строка 10 */ i = fl + 2*ch; /* строка 11 */ fl = 2.0*ch + 1; /* строка 12*/ printf(" ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); ch = 2.0e30; /* строка 14 */ printf(" Теперь ch = %с \n" , ch); } Выполнив программу "преобразования", получим следующие результаты: ch =A, i = 65, fl = 65.00 89 ch =B, i = 197, fl = 329.00 Теперь ch = Вот что происходит в программе. Строки 8 и 9: Величина ' А' присваивается символьной переменной ch. Переменная i получает целое значение, являющееся преобразованием символа ' А' в целое число, т. е ' 65'. И наконец, перемен ная fl получает значение 65.00, являющееся преобразованием числа 65 в число с плавающей точкой. Строки 10 и 13: Значение символьной переменной 'А' преобразуется в целое число 65, к которому затем добавляется 1. После этого получившееся в результате число 66 преобразуется в код символа В и помещается в переменную ch. Строки 11 и 13. При умножении на 2 значение переменной ch преобразуется в целое число (66). При сложении с величиной переменной fl получившееся в результате число (132) преобразуется в число с плавающей точкой. Результат (197.00) преобразуется в число целого типа и присваивается переменной i. Строки 12 и 13. Перед умножением на 2.0 значение переменной ch(' В') преобразуется в число с плавающей точкой. Перед выполнением сложения величина переменной i(197) преобразуется в число с плавающей точкой, а результат операции (329.00) присваивается переменной fl. Строки 14 и 15: Здесь производится попытка осуществить преобразование типов в порядке убывания старшинства - переменная ch полагается равной сравнительно большому числу. Результаты оказываются неутешительными. Независимо от переполнения и усечения, которые имеют место, в итоге на нашей системе мы пoлучили код, соответствующий какому-то непечатаемому знаку. На самом деле существует еще один вид преобразования типов. Для, сохранения точности вычислений при арифметических операциях все величины типа float преобразуются в данные типа double. Это существенно уменьшает ошибку округления. Конечный результат, естественно, преобразуется обратно в число типа float, если это диктуется соответствующим оператором описания. Вам нет не обходимости заботиться о выполнении подобных преобразований, но должно быть приятно сознавать, что компилятор стоит на страже ваших интересов. Операция приведения Далее Содержание Самое лучшее - это вообще избегать преобразования типов особенно в порядке убывания ранга. Но иногда оказывается удобным применять такие преобразования при условии, что вы ясно представляете себе смысл выполняемых действий. Преобразования типов, которые мы обсуждали до сих пор, выполнялись автоматически. Кроме того, существует возможность точно указывать тип данных, к которому необходимо привести некоторую величину. Этот способ называется "приведением" типов и используется следующим образом: перед данной величиной в круглых скобках записывается имя требуемого типа. Скобки и имя типа вместе образуют операцию приведения". В общем виде она записывается так (тип) где фактическое имя требуемого типа подставляется вместо слова "тип". Рассмотрим две нижеследующие строки, где mice - это переменная типа int. Вторая строка содержит две операции приведения mice = 1.6 + 1.7; mice = (int)1.6 + (int)l.7; В первом примере используется атоматическое преобразование типов. Сначала числа 1.6 и 1.7 складываются - результат равен 3.3. Затем путем отбрасывания дробной части полученное число преобpaзyeтcя в 3 для согласования с типом int переменной mice. Во втором примере 1.6 и 1.7 преобразуются в целые числа 1, так что переменной mice присваивается значение, равное 1+1, или 90 2. Вообще говоря, вы не должны смешивать типы; вот почему в некоторых языках это запрещено. Но бывают ситуации, когда это оказывается полезным. Философия языка Си заключается в том, чтобы не устанавливать барьеров на вашем пути, но при этом возложить на вас всю ответственность за злоупотребление предоставленной свободой. Резюме: операции в языке Си Ниже перечислены операции, которые мы уже обсудили. = Присваивает величину справа от знака переменной слева от него + Прибавляет величину справа от знака к величине слева от него - Вычитает величину справа от знака из величины слева от него - Унарная операция, изменяет знак величины справа от знака * Умножает величину справа от знака на величину слева от него / Делит величину слева от знака на величину справа от него. Результат усекается, если оба операнда целые числа % Дает остаток при делении величины слева от знака на величину справа от него (только для целых чисел) ++ Прибавляет 1 к значению переменной слева от знака (префиксная форма) или к значению переменной справа от знака (постфиксная форма) -- Аналогичная операции ++, но вычитает 1 sizeof Дает размер операнда, стоящего справа, в байтах. Операнд может быть спецификацией типа, заключенного в круглые скобки, как, например, sizeof (float), или именем конкретной переменной, массива и т. п., например sizeof foo (тип) Операция приведения: приводит величину, стоящую справа, к типу, определяемому ключевым словом (или словами) в скобках. Например, 91 (float)9 преобразует целое число 9 в число с плавающей точкой 9.0. ПРИМЕР ПРОГРАММЫ Далее Содержание На рис. 5.8 приведена программа, которая может оказаться полезной тем, кто занимается бегом, и которая иллюстрирует некоторые положения данной главы. Она выглядит довольно длинной, но все вычисления в ней выполняются шестью операторами, помещенными в конце. Большей частью программа занимается организацией диалога между машиной и пользователем. Мы ввели в программу достаточно большое число комментариев, чтобы сделать ее почти самодокументируемой. Просмотрите ее, а затем мы объясним некоторые ее моменты. /* бег */ # define SM 60 /* число секунд в минуте */ # define SH 3600 /* число секунд в часе */ # define МК 0.62137 /* число миль в километре */ main() { float distk, distm; /* дистанция в км и милях */ float rate; /* средняя скорость в милях в час */ int min, sec; /* время бега в минутах и секундах */ int time; /* время бега в секундах */ float mtime; /* время пробега одной мили в секундах */ int mmin, msec; /* время пробега одной мили в минутах и секундах */ printf(" Эта программа пересчитывает ваше время пробега дистанции, выраженной в км, \n"); printf(" во время, требуемое для пробега одной мили, и вашу среднюю \n"); printf(" скорость в милях в час,\n"); printf(" Укажите, пожалуйста, дистанцию в километрах.\n"); scanf(" %f ", &distk); printf(" Введите затем время в минутах и секундах. \n "); printf(" Начните с ввода минут. \n"); scanf(" %d", &min); printf(" Теперь вводите секунды. \n"); scanf(" %d", &sec); time = SM * mm + sec; /* переводит время в секунды */ distm = MK * distk; /* переводит километры в мили */ rate = distm / time*SH; /* число миль в сек * число сек в час = число миль в час */ mtime = (float)time / distm; /* время/дистанция = время на милю */ mmin = (int)mtime / SM; /* находит целое число минут */ msec = (int)mtime % SM; /* находит остаток в секундах */ printf("Bы пробежали %1.2f KM (%1.2f мили) за %d мин %d с \n", distk, distm, mm, sec); printf(" Эта скорость соответствует пробегу мили за %d : мин", mmin); printf("%d c.\n Ваша средняя скорость %l.2f миль/ч \n", msec, rate); } РИС. 5. 8. Программа, полезная для тех, кто занимается бегом Здесь мы применили тот же подход, который использовали в программе секунды в минуты для перевода времени, выраженного в секундах, в минуты и секунды. Кроме того, нам пришлось воспользоваться преобразованиями типов. Почему? Потому что для той части программы, которая занимается переводом секунд в ми нуты, нам требуются аргументы целого типа, а при преобразовании из метрической системы в мили используются числа с плавающей точкой. Мы применили операцию приведения для того, чтобы сделать эти преобразования явными. Честно говоря, нашу программу можно было бы написать, используя только автоматическое преобразование типов. Мы так и делали, применяя операцию приведения переменной mtime к типу int, чтобы при вычислении времени все операнды были целого типа. Однако такая версия 92 компилятора работает всего на одной из двух доступных нам систем. Использование операции приведения не только проясняет ваш замысел человеку, знакомящемуся c вашей программой, но и упрощает ее компиляцию. Вот результат работы данной программы. Эта программа пересчитывает ваше время пробега дистанции, выраженной в км, во время, требуемое для пробега одной мили, и вашу среднюю скорость, в милях в час. Укажите, пожалуйста дистанцию в километрах. 10,0. Введите затемвремя в минутах и секундах. Начните с ввода минут. 36. Теперь введите секунды. 23 Вы пробежали 10,00 км (6,21 мили) за 36 мин. 23 с. Эта скорость соответствует пробегу мили за 5 мин 51 с. Ваша средняя скорость 10.25 миль/ч ЧТО ВЫ ДОЛЖНЫ БЫЛИ УЗНАТЬ В ЭТОЙ ГЛАВЕ Далее Содержание Как использовать операции: +,-,*,/,%,++, --, (type). Что такое операнд: это - величина, над которой выполняется операция. Что такое выражение: совокупность операций и операндов. Как вычислять значение выражения: в соответствии с порядком старшинства. Как распознать оператор: по символу. Виды операторов: операторы описания, присваивания, while, составной. Как сформировать составной оператор: заключить последовательность операторов в фигурные скобки {}. Как сформируется оператор while: while (проверка условия) оператор. Как вычисляются выражения со смешанными типами данных: с помощью автоматического преобразования типов. ВОПРОСЫ И ОТВЕТЫ Далее Содержание 1. Предположим, все переменные имеют тип int. Определите значение каждой из последующих переменных: а. х = (2+3)*6, б. х = (12+6)/2*3, в. y = x = (2+3)/4, г. y = 3 + 2*(x = 7/2 ), д. x = (int)3.8 + 3.3, 2. Мы подозреваем, что в программе, приведенной ниже, имеется несколько ошибок. Сумеете ли вы помочь нам их обнаружить? main( ) { int i = 1, float n; printf(" Внимание! Сейчас появится несколько дробей. \n"); while (i < 30) n = 1/ i; printf(" %f", n); printf(" Вот и все! Конец! \n"), } 93 3. Ниже приведена первая попытка сделать программу "секунды в минуты" диалоговой. Программа нас не удовлетворяет. Почему? Как ее улучшить? #define SM 60 main( ) { int sec, mm, left, printf( Эта программа переводит секунды в минуты и секунды \n ); printf( 'Укажите число секунд \n ), printf( Для окончания работы программы необходимо ввести 0 \n); while (sec < 0){ scanf( %d", &sec), mm = sec/SM, left = sec % SM, printf("%d с это % d мин %d с \n", sec, mm, left), printf(" Введите следующее значение \n"), } printf( До свидания!\n ), } Ответы 1. а. 30 б. 27(а не 3). Результат 3 можно получить в случае (12 + 6)/(2*3) в. х = 1, у = 1 (деление целых чисел) г. х = 3 (деление целых чисел) и у = 9 д. х = 6, так как (int)3.8=3.3 + 3.3 = 6.3, это число будет преобразовано в число 6, поскольку х имеет тип int 2. Строка 3: должна оканчиваться точкой с запятой, а не запятой. Строка 7: оператор while представляет собой бесконечный цикл, потому что величина переменной i остается равной 1 и всегда будет меньше 30. По всей видимости, мы собирались написать while(i+ + < 30). Строки 7-9: отступы в строках подразумевают, по видимому, что из операторов, расположеных в строках 8 и 9, мы намеревались сформировать блок, но отсутствие фигурных скобок означает, что цикл while включает в себя только оператор, расположенный на строке 8; поэтому фигурные скобки должны быть обязательно добавлены. Строка 8: поскольку 1 и i - оба целого типа, результат деления будет равен 1 при i, равном 1, и 0 - для всех больших значений. Необходимо писать так n = 1.0/i; перед делением значение переменной i будет приведено к типу данных с плавающей точкой и будет получен ненулевой результат. Строка 9: мы опустили символ "новая строка" в управляющей строке; это приведет к тому, что числа будут печататься на одной строке, если так допускается устройством вывода. 3. Основная трудность лежит в согласовании между оператором, выполняющим проверку (величина переменной sec больше 0 или нет?), и оператором scanf( ), осуществляющим ввод значения переменной sec. В частности, когда проверка выполняется первый раз, переменная sec в программе еще не получает своего значения, и поэтому сравнение будет производиться с некоторой случайной величиной (мусором"), которая может оказаться в соответствующей ячейке памяти. Одно решение, хотя и некрасивое, заключается в инициализации переменной sec, скажем величиной 1, в результате чего первый раз сравнение выполнится. Но здесь обнаруживается вторая проблема. Когда при окончании работы мы набираем величину 0, чтобы остановить программу, оказывается, что значение переменной |