А.А. Белеванцев, С.С. Гайсарян, Л.С. Корухова, Е.А. Кузьменкова, В.С. Махнычев. Семинары по курсу Алгоритмы и алгоритмические языки (1108027), страница 3
Текст из файла (страница 3)
Такими точками в программеявляются:9конец полного выражения (т.е. для выражения a + b - c*d послевычисления всего выражения);при выполнении операции x , y – между вычислением x и y;при выполнении операции z ? x : y – между вычислением z ивычислением x либо y;при вызове функции – перед выполнением ее тела и после вычисления ееаргументов;при выполнении операций x && y и x || y – между вычислением x ивычислением y.Из-за нарушения семантики точек последовательных вычислений выражения типа(a=2) + (a=3), i++ + ++i являются некорректными (дважды модифицируетсяпеременная), аналогично и выражения типа a = b++ + b (значение b читается как дляопределения нового значения b, так и для вычисления суммы).
Значение таких выраженийне определено (undefined), т.е. стандарт языка не требует от компилятора Си получитькакое-то конкретное значение. Поэтому для разных компиляторов из-за особенностейвыполняемых ими оптимизаций значения таких выражений будут различны.Использование неопределенных выражений крайне не рекомендуется: программа с нимибудет получать неожиданные для программиста результаты или разные результаты взависимости от случайных факторов.2.2.
Логические выраженияЛогическими операциями являются отрицание "!", конъюнкция "&&" идизъюнкция "||". Результатом этих операций является единица, если соответствующаялогическая функция истинна, и ноль, если ложна. Для скалярных операндов (т.е.целочисленных, вещественных и указателей) истинным считается любое ненулевоезначение, а ложью – лишь ноль.Благодаря тому, что после вычисления первого операнда операций "&&" и "||"находится точка последовательных вычислений, становится возможным использования«короткой» логики: операнды вычисляются слева направо, и если результат операции ужеизвестен (значение первого операнда равно 0 для конъюнкции и 1 для дизъюнкции),вычисление второго операнда не производится.Например, в выражении if (n != 0 && b > a/n) деления на ноль невозникнет, так как в этом случае вычисления второго операнда конъюнкции не будет.
Вдругом примере if ((c = getchar ()) != EOF && isprint (c)) вызовфункции isprint произойдет только в том случае, если функция getchar вернетзначение, отличное от EOF, при этом гарантируется, что переменная c будет уже измененав ходе вычисления побочных эффектов первого операнда конъюнкции.Задача. Написать эквивалентное выражение, не содержащее операции отрицания"!":a) ! (a>b)б) ! (2*aв) ! (a<2г) ! (a<bд) ! (a<1==||&&||b+4)a>5)c<d)b<2 && c<3).Решение.10a) a <= bб) 2*a != b+4в) a>=2 && a<=5г) a>=b || c>=dд) a>=1 && (b>=2 || c>=3).2.3. Побитовые операцииПобитовые, или поразрядные, операции работают над двоичным представлениемчисла. Одноместными побитовыми операциями является инверсия или побитовоеотрицание “~", двухместными – побитовое И "&", побитовое включающее ИЛИ "|",поразрядное исключающее ИЛИ "^", побитовые сдвиги влево "<<" и вправо ">>".
Сдвигна отрицательное число бит или на число, превосходящее ширину первого операнда, неопределен.Нужно отметить, что при сдвиге вправо беззнакового операнда освобождающиесябиты заполняются нулями, тогда как для знаковых операндов возможно заполнение какнулями (логический сдвиг), так и значением знакового бита (арифметический сдвиг) –выбор точного поведения зависит от реализации. Поэтому для побитовых вычисленийрекомендуется всегда использовать беззнаковые типы.Пример. Пусть переменная x имеет тип unsigned int.
Чтобы обнулить все битыx, кроме трех младших, необходимо использовать побитовую операцию &, взяв в качествевторого параметра число, у которого лишь три младших бита установлены: это число 7,т.е. ответом является выражение x & 7. Аналогично, для обнуления всех бит, кроме трехмладших, вторым параметром побитового И должно быть число с установленными всемибитами, кроме трех младших, то есть ~7, все выражение есть x & ~7. Для установкидвух младших бит используется включающее ИЛИ: x | 3.
Необходимую константуможно также получить сдвигом: для установки пяти младших бит вместо запоминанияконстанты 31 используют выражение x | ((1 << 5) - 1).2.4. Старшинство операцийСтаршинство операций задается таблицей приоритетов (см. рис. 2) и, как правило,соответствует ожиданиям программиста, избавляя его от необходимости писать лишниескобки в сложных выражениях.ОперацииАссоциативность( ) [ ] -> .! ~ ++ -- + - sizeof (type) *&* / %+ << >>< <= > >=== !=&^|&&||?:Слева направоСправа налевоСлева направоСлева направоСлева направоСлева направоСлева направоСлева направоСлева направоСлева направоСлева направоСлева направоСправа налево11Справа налево= += -= *= /= %= &= ^= |= <<=>>=,Слева направоРис.2.
Старшинство операций в Си.Приоритет операций именования памяти (доступа к членам структуры, элементаммассива, вызова функции) наивысший. Далее, приоритет одноместных операций выше,чем двухместных. Приоритет двухместных мультипликативных операций, в том числелогических и побитовых, выше, чем соответствующих аддитивных: приоритет "* / %"выше, чем у "+ -"; приоритет "&&" выше "||" , а приоритет "&" выше, чем "^" и "|".Приоритет отношений ("< <= > >= == !=") ниже, чем арифметических операций исдвигов. Приоритет операции присваивания, в том числе укороченных присваиваний,самый низкий, за исключением операции “запятая”.Пример.
В выраженииx = 3*a + b/c >= d скобки естественно расставляются какx = ((3*a) + (b/c)) >= d. Тем не менее, рекомендуется употреблятьскобки, если они улучшают ясность и читаемость кода, либо в случае сомненийпрограммиста.2.5. Задачи для самостоятельного решения2.5.1. Целой переменной k присвоить значение, равное третьей от конца цифре взаписи целого положительного числа x.2.5.2.
Целой переменной k присвоить значение, равное сумме цифр в записи целогоположительного трехзначного числа x.2.5.3. Целой переменной k присвоить значение, равное первой цифре дробнойчасти в записи вещественного положительного числа x.2.5.4. Определить число, полученное выписыванием в обратном порядке цифрзаданного целого трехзначного числа.2.5.5. Используя условную операцию “?:”, написать выражение для вычисления:a) модуля целого числа x,б) максимума двух целых чисел,в) максимума трех целых чисел.2.5.6. Пусть int a = 5, int b = 7. Чему будет равно значение выражения ипобочные эффекты?а) (a += 5) * (b -= 3)б) --b / (a++ - 3)в) (a -= 2) || 47 / (b - 7)г) (a *= b) + (b *= a)2.5.7.
Пусть int x = 10 ; int y = 20 ;Для приведенного выражения указать его значение и побочные эффекты (если ониесть) либо “ошибка”, если выражение ошибочноа) x += y , y += x, y, xб) y+=(x= 1) + (x12= 2)в)x +=((y=1)&&(y=2))г) y %= x / 6д) x || ++yе) x ? !x : y2.5.8. Пусть определены переменные k и x, имеющие тип unsigned int.Считая, что в x находится трехзначное число, выпишите присваивание, котороепоместит в k это число в «перевернутом» виде. Например, если в x содержалось 123, то вk следует поместить число 321.2.5.9. Пусть определены переменные double d; int i; Укажите какиезначения будут иметь переменные i и d после вычисленияi = d = 10/4;Пояснение.
В задачах 2.5.10-2.5.15 cчитать, что тип unsigned int занимает 32бита, а тип char – 8 бит.2.5.10. «Упаковать» четыре символа в 32-битное беззнаковое целое.2.5.11. «Распаковать» 32-битное беззнаковое целое число в четыре символа.2.5.12. Реализовать операции для типа «множество» (отображение номеровэлементов множества на разряды слова, количество элементов множества заданоразмером слова): а) объединение, пересечение, вычитание, дополнение, сравнениемножеств; б) добавить/удалить элемент в/из множества; является ли множество пустым; в)выбрать произвольный элемент из множества.2.5.13. Не используя дополнительных переменных, поменять местами значениядвух переменных типа int.2.5.14.
Поменять знак переменной x типа int.2.5.15. Инвертировать 5 младших битов переменной x типа unsigned int,остальные биты оставить без изменения.3. Ввод/вывод.Символьный и форматный ввод-вывод данных. Операторы: выражение-оператор,условный оператор, оператор выбора, операторы цикла, оператор перехода, составнойоператор.Функции ввода-вывода в Си являются частью стандартной библиотеки языка, дляих использования нужно подключать заголовочный файл stdio.h (см. раздел 1). В этомразделе познакомимся с некоторыми функциями, оперирующими со стандартнымипотоками ввода и вывода (которые обычно связаны с клавиатурой и дисплеемсоответственно).
Функции стандартной библиотеки, предназначенные для работы сфайлами, описаны в разделе 9.Функциями символьного ввода-вывода (т.е. считывающими и записывающимиодин символ) являются функции getchar и putchar со следующими объявлениями:int getchar(void); и int putchar(int c);. Обе функции возвращаютсчитанный либо записанный символ типа unsigned char, приведенный к int, либоспециальное значение EOF, если достигнут конец потока ввода либо произошла другая13ошибка ввода-вывода.
Обратите внимание, что из-за необходимости различать верносчитанный символ и состояние ошибки функции не могут возвращать значение типа charили unsigned char, поэтому используется значение типа int. Типичный цикл,считывающий по одному символу из стандартного потока ввода до достижения его конца,выглядит так:int c;while ((c = getchar ()) != EOF){/* обработать следующее значение с */...}Функции форматного ввода-вывода scanf и printf (с прототипамиint scanf(const char *format, ...); иint printf(const char *format, ...);)могут считывать и записыватьданные любых базовых типов и строки согласно заданной форматной строке, котораяявляется первым аргументом функций. Форматная строка задает количество и типзначений, которые необходимо считать либо записать, а остальные аргументы являютсяуказателями на объекты памяти, куда нужно поместить считанные значения, либовыражениями, вычисляющие значения для записи.
Формат одного значения задаетспецификатор ввода-вывода, начинающийся с символа %. Часто используемымиспецификаторами являются: %d, %ld, %lld – напечатать/считать число типа int, long, long long; %u, %lu, %llu – напечатать/считать число типа unsigned,unsigned long, unsigned long long; %f, %Lf – напечатать число типа double, long double; %f, %lf, %Lf – считать число типа float, double, long double; %c – напечатать/считать символ; %s – напечатать/считать строку (при вводе строка считывается до первогопробельного символа); %% – напечатать знак процента.Остальные символы форматной строки при выводе сохраняются в том же виде, чтоможет использоваться для выдачи сообщений или перехода на новую строку. Например,следующий вызов выводит два целых числа и их сумму, переводя вывод на новую строку:printf ("%d %d %d\n", a, b, a + b);.