Файл: Лекция использование основных операций и выражений языка сС. Стандартные функции и директивы препроцессора операции и выражения СС Классификация операций.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 07.12.2023

Просмотров: 32

Скачиваний: 5

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.


59
Лекция 5. ИСПОЛЬЗОВАНИЕ
ОСНОВНЫХ ОПЕРАЦИЙ И ВЫРАЖЕНИЙ
ЯЗЫКА С/С++. СТАНДАРТНЫЕ ФУНКЦИИ
И ДИРЕКТИВЫ ПРЕПРОЦЕССОРА
5.1. Операции и выражения С/С++
Классификация операций
Операции бывают
унарные
(воздействуют на одно значение или выражение),
бинарные
(участвуют два выражения) и
тернарные
(три выражения). Основные унарные операции приведены в табл. 5.1.
Унарные операции выполняются справа налево.
Таблица 5.1
Унарные операции
Операции
Назначение
&
Получение адреса операнда
*
Обращение по адресу (разыменование)

Унарный минус, меняет знак арифметического операнда

Поразрядное инвертирование внутреннего двоичного кода (побито- вое отрицание)
!
Логическое отрицание (НЕ). В качестве логических значений ис- пользуется 0 – ложь и не 0 – истина, отрицанием 0 будет 1, отрица- нием любого ненулевого числа будет 0
++
Инкремент или увеличение на единицу: префиксная операция – уве- личивает операнд до его использования, постфиксная операция уве- личивает операнд после его использования.
--
Декремент или уменьшение на единицу: префиксная операция –
уменьшает операнд до его использования, постфиксная операция уменьшает операнд после его использования sizeof Вычисление размера (в байтах) для объекта того типа, который имеет операнд
Основные бинарные операции приведены в табл. 5.2, они выпол- няются слева направо.
Таблица 5.2
Бинарные операции
Операции
Назначение
+
Бинарный плюс (сложение арифметических операндов)

Бинарный минус (вычитание арифметических операндов)
Аддитивные

60
Окончание табл. 5.2
Операции
Назначение
*
Умножение операндов арифметического типа
/
Деление операндов арифметического типа (ес- ли операнды целочисленные, то выполняется целочисленное деление)
%
Получение остатка от деления целочисленных операндов (операция по модулю)
Мультипликатив- ные
<<
Сдвиг влево битового представления значения левого целочисленного операнда на количество разрядов, равное значению правого операнда
>>
Сдвиг вправо битового представления значения правого целочисленного операнда на количест- во разрядов, равное значению правого операнда
Операции сдвига
(определены толь- ко для целочислен- ных операндов)
&
Поразрядная конъюнкция (И) битовых пред- ставлений значений целочисленных операндов
|
Поразрядная дизъюнкция (ИЛИ) битовых пред- ставлений значений целочисленных операндов
^
Поразрядное исключающее ИЛИ битовых пред- ставлений значений целочисленных операндов
Поразрядные опера- ции
<
Меньше, чем
>
Больше, чем
<=
Меньше или равно
>=
Больше или равно
==
Равно
!=
Не равно
Операции сравнения
&&
Конъюнкция (И) целочисленных операндов или отношений, целочисленный результат ложь (0) или истина (1)
||
Дизъюнкция (ИЛИ) целочисленных операндов или отношений, целочисленный результат ложь (0)
или истина (1)
Логические бинар- ные операции
=
Присваивание, присвоить значение правого операнда левому
+=
–=
*=
/=
%=
|=
&=
^=
<<=
>>=
Выполнить соответствующую операцию с ле- вым операндом и правым операндом и присво- ить результат левому операнду
Операции присваи- вания и составного присваивания

61
Приоритеты операций приведены в табл. 5.3.
Таблица 5.3
Приоритеты операций
Ранг
Операции
1
( ) [ ] –> .
2
! – ++ -- & * (тип) sizeof тип ( )
3
* / % (мультипликативные бинарные)
4
+ - (аддитивные бинарные)
5
<< >> (поразрядного сдвига)
6
< > <= >= (отношения)
7
== != (отношения)
8
& (поразрядная конъюнкция «И»)
9
^ (поразрядное исключающее «ИЛИ»)
10
| (поразрядная дизъюнкция «ИЛИ»)
11
&& (конъюнкция «И»)
12
|| (дизъюнкция «ИЛИ»)
13
?: (условная операция)
14
= *= /= %= -= &= ^= |= <<= >>= (операция присваивания)
15
, (операция запятая)
В отличие от унарных и бинарных операций в
тернарных услов-
ных операциях
используется три операнда:
Выражение1 ? Выражение2 : Выражение3;
Первым вычисляется значение выражения 1. Если оно истинно, то вычисляется значение выражения 2, которое становится результа- том. Если при вычислении выражения 1 получится 0, то в качестве ре- зультата берется значение выражения 3.
Выражения
Комбинация знаков операций и операндов, результатом которой яв- ляется определенное значение, называется
выражением
. Каждый операнд в выражении может быть выражением. Значение выражения зависит от расположения знаков операций и круглых скобок в выражении, а также от приоритета выполнения операций. Каждое выражение состоит из од- ного или нескольких операндов, символов операций и ограничителей: x<0 ? -x : x ; //вычисляется абсолютное значение x
X * 12 + Y val < 3
-9

62
Выражение, после которого стоит точка с запятой – это оператор- выражение.
Рассмотрим подробнее некоторые операции и варианты их ис- пользования.
Операция присваивания
(
=
) рассматривается как выражение, имею- щее значение левого операнда после присваивания. Присваивание может включать несколько операций присваивания, изменяя значения нескольких операндов, например:
Недопустимыми являются:
присваивание константе, присваива-
ние функции и присваивание результату операции
Операции инкремента и декремента
(
++, --
)
относятся к унар- ным арифметическим операциям, которые служат соответственно для увеличения или уменьшения значения, хранимого в переменной целого типа. Например, следующие три оператора дадут один и тот же эффект:
Операции инкремента и декремента не только изменяют значения переменных, но и возвращают значения. Таким образом, их можно сделать частью более сложного выражения:
Имеется
постфиксная
и
префиксная
форма операторов инкремента и декремента. В постфиксной форме записи переменная, к которой int i, j, a; float m, y; m *= y;
//m = m * y; i += 2;
//i = i + 2; m /= y + 1;
//m = m / (y + 1);
--a;
//a = a - 1; j = i++;
//j = i; i = i + 1; j = ++i;
//i = i + 1; j = i; sum = sum + 1; sum += 1;
++sum;
2 = a + b;
//ошибка getch() = a;
//ошибка
(a + 1) = 2 + b;
//ошибка long a; char b; int c, x, y, z; a = b = c;
//эквивалентно b = c; a = b; x = i + (y = 3) - (z = 0);
//z = 0, y = 3, x = i + y - z;

63 применена операция, увеличивается (или уменьшается) только после того, как ее значение будет использовано в контексте.
Типичной ошибкой является попытка использовать в операции инкремента или декремента операнд, отличный от имени простой переменной:
Общий вид операций сравнения (отношения):
<выражение 1> <знак операции> <выражение 2>
Выражениями могут быть любые базовые (скалярные) типы. Зна- чения выражений перед сравнением преобразуются к одному типу.
Результат операции сравнения – значение 1, если отношение истинно, или 0 в противном случае (ложно). Операция сравнения может ис- пользоваться в любых арифметических выражениях: или
В
С/С++
предусмотрены
битовые
операции
для работы с
отдельными битами. Эти операции нельзя применять к переменным вещественного типа.
Операндами операций над битами могут быть только выражения, приводимые к целому типу. Операции (

,
&
,
|
,
^
)
выполняются пораз- рядно над всеми битами операндов (знаковый разряд не выделяется).
Общий вид операции инвертирования:
<выражение>
Остальные операции над битами имеют вид:
<выражение 1> <знак операции> <выражение 2>
++(x + 1); //ошибка
(
a < b && b < c)
//если ОДНОВРЕМЕННО ОБА a < b
//и b < c, то истина, иначе ложь if (a == 0 || b > 0)
//если ХОТЯ БЫ ОДИН a==0
//или b > 0, то истина, иначе ложь
!0
//1
!10
//0
!((x = 1) < 0)
//1 0 < x < 100
//ошибка
(0 < x) && (x < 100)
//верно int b = 5; int c = 10; a = b > c;
//Запомнить результат сравнения a=0 a = (b > c)* 2;
//a= 0

64
Ниже приведена таблица истинности логических операций
&
,
|
и
^
Операнд 1
Операнд 2
& | ^
0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 1 1 1 0
Операция
&
часто используется для маскирования некоторого множества бит.
Операция
!
используется для включения (устанавли- вает в единицу те биты, которые были нулями).
Необходимо отличать побитовые операции
&
и
!
от логических бинарных операций
&&
и
||
Арифметические операции
задают обычные действия над операн- дами арифметического типа.
Если арифметическая операция содержит операнды различных ти- пов, то компилятор выполняет автоматическое преобразование их типов.
Часто арифметические операции используются для обработки чи- сел, например:
Операции сдвига
<<
и
>>
осуществляют соответственно сдвиг вправо и влево своего левого операнда на число позиций, задаваемых правым операндом. Операции сдвига выполняются также для всех разрядов с потерей выходящих за границы бит. int n = 12345; int low, i = 6; low = n %10;
//младшая цифра числа n n = n / 10;
//отбросить младшую цифру числа n if ( n % i == 0)... //определить - n делится нацело на i ? n = n * 10 + i;
//добавить цифру i к значению числа n i % j
//i - ( i/j ) * j
12 % 6
//0 13 % 6
//1 x = 1; y = 2; x & y
//результат 0, т.к. 0001 & 0010=0000 x && y
//результат 1, т.к. в операц. сравнения
//оба операнда истина

65
Если левостоящее выражение имеет тип unsigned
, то при сдвиге вправо освобождающиеся разряды гарантированно заполняются ну- лями (логический сдвиг). Выражения типа signed могут, но не необя- зательно, сдвигаться вправо с копированием знакового разряда
(арифметический сдвиг). При сдвиге влево освобождающиеся разряды всегда заполняются нулями. Если правостоящее выражение отрица- тельно либо больше длины левостоящего выражения в битах, то ре- зультат операции сдвига не определен.
Операции сдвига вправо на
k
разрядов можно использовать для деления, а сдвиг влево – для умножения целых чисел на 2 в степени
k
:
Операция
sizeof выполняется на этапе компиляции программы и дает константу, которая равна числу байтов, требуемых для хранения в памяти данного объекта. Объектом может быть имя переменной, массива, структуры или просто спецификация типа.
Операторы
Основной источник операторов в программе – выражения. Любое из них, ограниченное символом
;
, превращается в оператор.
Запись действий, которые должен выполнить компьютер, состоит из операторов. При выполнении программы операторы выполняются один за другим, за исключением операторов управления, которые могут изменить последовательное выполнение программы. Различают опера- торы объявления переменных, операторы управления и операторы- выражения. Простейшей формой оператора является пустой оператор:
;
Он ничего не делает. Однако он может быть полезен в тех случаях, когда синтаксис требует наличие оператора, а он не нужен.
Любая последовательность операторов, заключенная в фигурные скобки
{}
, может выступать в любой синтаксической конструкции как один
составной оператор
(
блок
): int i; cout << sizeof(int) << sizeof(i); x << 1; //x * 2 x >> 1; //x / 2 x << 3; //x * 8 0x81 << 1
//10000001<<1=00000010=0x02 0x81 >> 1
//10000001>>1=01000000=0x40

66
Он позволяет рассматривать несколько операторов как один. Область видимости имени, описанного в блоке, простирается до конца блока.
Операция запятая
(
,
) используется при организации строго га- рантированной последовательности вычисления выражений (исполь- зуется там, где по синтаксису допустима только одна операция, и для организации множественных выражений, расположенных внутри круглых скобок). Форма записи: выражение 1, …, выражение N;
Выражения вычисляются слева направо. Например:
Выражения,
разделенные запятой,
не должны быть присваиваниями.
Ниже приведенный пример не является примером хорошего кода:
5.2. Преобразование типов
В операциях могут участвовать операнды различных типов, в этом случае они
неявно
преобразуются к общему типу в порядке увеличения их объема памяти, необходимого для хранения их значе- ний. Поэтому неявные преобразования всегда идут от «меньших» объектов к «большим». Схема выполнения преобразований операндов арифметических операций: short, char → int → unsigned → long → double float → double
{ a = b + 2; b++;
}
int one = 1, two = 2, three = 100, six = 0; six = (++one, ++two, ++three);
//one = 2, two = 3, three = 101, six = 101 char X, Y;
(X = Y, Y = getch());
//присваивает переменной X значение Y, считывает символ,
//вводимый с клавиатуры, и запоминает его в Y int i, j, k, n; m = ( i = 1, j = i++, k = 6, n = i + j + k);
//i = 1, j = i = 1, i = 2, k = 6, n = 2 + 1 + 6, m = n = 9

67
Значения типов char и short всегда преобразуются в int
; если лю- бой из операндов имеет тип double
, то второй преобразуется в double
; если один из операндов long
, то другой преобразуется в long
При присваивании значение правой части преобразуется к типу левой, который и является типом результата. При некорректном ис- пользовании операций присваивания могут возникнуть ошибки.
В любом выражении преобразование типов может быть осущест- влено
явно
, в С для этого достаточно перед выражением поставить в скобках идентификатор соответствующего типа:
(тип) выражение;
В результате значение выражения преобразуется к заданному типу:
Эта форма является устаревшей и сохранена в стандарте С++ только для обеспечения обратной совместимости с программами, на- писанными для С и предыдущих версий С++.
В С++ явное преобразование типов производится при помощи следующих операторов: static_cast
, dynamic_cast
, const_cast и reinterpret_cast
. Хотя иногда явное преобразование необходимо, оно служит потенциальным источником ошибок.
Явное преобразование типов используется для разыменования ука- зателя void*
, для того, чтобы избежать стандартного преобразования или выполнить вместо него собственное. Также может использоваться, float a; int i = 6, j = 4; a = (i + j) / 3;
// a = 3 a = (float)(i + j) / 3; →
// a = 3.333333 int ival; float fval; double dval; ival + fval + dval;
//fval и iva1 преобразуются к double перед сложением float x; int i; x = i; i = x;
//float преобразуется в int, дробная часть отбрасывается

68 чтобы избежать неоднозначных ситуаций, в которых возможно не- сколько вариантов применения правил преобразования по умолчанию.
Синтаксис операции явного преобразования: cast-name< type >( expression)
;
Здесь cast-name
– одно из ключевых слов static_cast
, const_cast
, dynamic_cast или reinterpret_cast
, а type
– тип, к которому приводится выражение expression
Так const_cast слу- жит для трансформации константного типа в неконстантный. Любое иное использование const_cast вызывает ошибку компиляции, как и попытка подобного приведения с помощью любого из трех других операторов. С применением static_cast осуществляются те преоб- разования, которые могут быть сделаны неявно, на основе правил по умолчанию:
Оператор reinterpret_cast работает с внутренними представ- лениями объектов (
reinterpret
– другая интерпретация того же внут- реннего представления), причем правильность этой операции целиком зависит от программиста.
Оператор dynamic_cast применяется при идентификации типа во время выполнения (
runtime type identification
).
5.3. Стандартные математические функции
В любой программе, кроме операторов и операций, используются средства библиотек, входящих в среду программирования, которые облегчают создание программ. Часть библиотек стандартизована и поставляется с компилятором. В стандартную библиотеку входят функции, макросы, глобальные константы. Это файлы, хранящиеся в папке include
Стандартные математические функции
Математические функции языка С декларированы в файлах
<сmath>
и

В большинстве приведенных здесь функций аргументы x
, y
и ре- зультат выполнения имеют тип double
. В табл. 5.5 приведены основные математические функции С/C++. const double d = 97.0; char ch = static_cast< char >( d );

69
Таблица 5.5
Основные математические функции С/C++
Описание содержится в math.h|cmath double ceil(double x); float ceilf(float x); long double ceill(long double x);
Функции округления до наименьшего целого, не меньшего, чем аргумент double cos(double x);
Возвращает значение косинуса x, где x – это значение в радианах (2
π ради- ан = 3600) double exp(double x);
Возвращает значение числа e, возве- денного в степень x double fabs(double x); float fabsf(float x); long double fabsl(long double x);
Абсолютное значение числа с пла- вающей точкой double floor(double x); float floorf(float x); long double loorl(long double x);
Наибольшее целое значение, но не большее x double fmod(double x, double y);
Функция получения остатка от деле- ния (с плавающей точкой) double log(double x);
Возвращает натуральный логарифм x double log10(double x);
Возвращает десятичный логарифм x double pow(double x, double y);
Возвращает значение x в степени y int rand(void);
Возвращает псевдослучайное число в диапазоне от нуля до
RAND_MAX
void srand(unsigned int seed);
Устанавливает свой аргумент как основу (
seed
) для новой последова- тельности псевдослучайных целых чисел, возвращаемых функцией rand()
double sin(double x);
Возвращает значение синуса аргу- мента x, где x указан в радианах double sqrt(double x);
Функция вычисления квадратного корня double tan(double x);
Возвращает тангенс аргумента x, где x задан в радианах
Пример
В прил. 4 содержится описание заголовочных файлов и стандарт- ных функций языка С++.
5.4. Директивы препроцессора
Директивы препроцессора представляют собой инструкции, записан- ные в исходном тексте программы и предназначенные для выполнения
Z = pow(x,10.0) + 3.7 * pow(x,8.0); //Z = x
10
+ 3.7·x
8

70 препроцессором языка. Директивы начинаются со специального знака
#
, помещаемого в первой позиции строки.
Директивы позволяют:
• описывать макросы, которые уменьшают трудоемкость написания программы и делают текст программы удобочитаемым и выразительным;
• включать текст из других текстовых файлов, содержащих про- тотипы библиотечных и разработанных пользователем функций, шаб- лоны структурных переменных и т.д.;
• организовывать условную компиляцию, т.е. в зависимости от заданных в командной строке или среде параметров получать раз- личный программный код.
Директива препроцессора
#include
Заголовочные файлы включаются в текст программы с помощью директивы препроцессора
#include
. Директива применяется для включения копии указанного в директиве файла в то место, где нахо- дится эта директива. Имя файла может быть указано двумя способами:
Различие между ними заключается в методе поиска препроцессо- ром включаемого файла. Если имя файла заключено в угловые скобки
(
< >
), считается, что используется стандартный заголовочный файл, и компилятор ищет этот файл в предопределенных местах. Двойные ка- вычки означают, что заголовочный файл – пользовательский, и его по- иск начинается с того каталога, где находится исходный текст проекта.
Заголовочные файлы содержат объявления и определения (клас- сов, структур, объединений, перечисляемых типов и прототипов функций), общие для различных программных файлов, и поэтому час- то создаются и включаются в файлы программ.
Символические константы и макроподстановка
Директива препроцессора
#define обычно используется для за- мены часто используемых в программе констант, ключевых слов, опе- раторов и выражений осмысленными идентификаторами.
Идентификаторы, которые заменяют числовые или текстовые константы либо произвольную последовательность символов, назы- ваются именованными константами.
Идентификаторы, которые представляют некоторую последова- тельность действий, заданную операторами или выражениями языка, называются макроопределениями.
#include
#include "file.h"

71
Формат директивы определяется как:
#define идентификатор строка_текста
Директива обеспечивает замену встречающегося в тексте про- граммы идентификатора на соответствующую строку текста, в том числе и с параметрами. Например:
#define SIZE 100 //символическая константа
#define min(a,b) ((a) < (b) ? (a) : (b)) //макрос
#define PRN(number) printf(#number " = %d\n", number);
//макрос int scale = 25, param = 10;
PRN(scale);
PRN(param); result = min(44,uplimit);
//result = ((44) < (uplimit) ? (44) : (uplimit));
Как и в случае символических констант, идентификатор макроса заменяется на замещающий текст до начала компиляции программы.
Макросы без параметров обрабатываются подобно символическим константам. Если макрос имеет параметры, то сначала в замещающий текст подставляются значения параметров, а затем этот расширенный макрос подставляется в текст вместо идентификатора макроса и спи- ска его параметров.
Определения символических констант и макросов могут быть ан- нулированы при помощи директивы
#undef
Она отменяет самое по- следнее определение поименованного макроопределения.
В С++ отдается предпочтение использованию именованных пере- менных типа const
, а не символических констант. Константные пере- менные являются данными определенного типа и их имена видны от- ладчику. Если используется символическая константа, то после того, как она была заменена на текст, только этот текст будет виден отлад- чику. Недостатком переменных типа const является то, что им требу- ется память в объеме, соответствующем их типу. Для символических констант дополнительной памяти не требуется.
#define TRI 3
#define F 5
#undef TRI //TRI теперь не определен
#define F 10 //F переопределен как 10

72
Условные директивы
Заголовочный файл, который подключается к модулю проекта, также может содержать директивы
#include
. Поэтому некоторые заго- ловочные файлы могут оказаться включенными несколько раз. Избе- жать этого позволяют условные директивы препроцессора. Например:
Условная директива
#ifndef проверяет, не было ли значение
FILE_H
определено ранее (
FILE_H
– это константа препроцессора; та- кие константы принято писать заглавными буквами). Препроцессор обрабатывает следующие строки вплоть до директивы
#endif
. В про- тивном случае он пропускает строки от
#ifndef до
#endif
Другим распространенным примером применения условных ди- ректив препроцессора является включение в текст программы отла- дочной информации.
Рассмотрим еще пример:
Директива
#ifdef сообщает, что если последующий идентифика- тор
O
определяется препроцессором, то выполняются все последую- щие директивы вплоть до первого появления
#else или
#endif
. Когда в программе есть
#else
, то программа от
#else до
#endif будет вы- полняться, если идентификатор не определен.
Макрос
assert
Макрос assert
, определенный в заголовочном файле

, выполняет проверку значения выражения. Если значение выражения
0 (ложь), то макрос assert выводит сообщение об ошибке и вызывает функцию abort
(из библиотеки утилит общего назначения

), которая завершает выполнение программы. Макрос удобно использо- вать при отладке. Например, переменная х
в программе не должна при- нимать значение большее, чем 10. В этом случае макрос assert можно
#ifdef O
#include "o.h" //выполнится, если O определен
#define ST 10
#else
#include "w.h" //выполнится, если O не определен
#define ST 20
#endif
#define FILE_H
/* содержимое файла file.h */
#ifndef FILE_H
#endif

73 использовать для проверки значения х
и вывода сообщения об ошибке, если значение х вышло из допустимого диапазона:
Если х
будет иметь значение, большее, чем 10, то программа вы- даст сообщение об ошибке, содержащее номер строки и имя файла, после чего завершит свою работу.
После того, как в тексте программы объявляется символическая константа
NDEBUG
, все последующие вызовы макроса assert будут игнорироваться. Таким образом, когда все они будут больше не нуж- ны (т.е. когда отладка закончена), в начале программы достаточно до- бавить строку вместо ручного удаления каждого макроса assert
5.5. Практические задания
Для каждой из задач составить блок-схему алгоритма.
1.
Дано действительное число
x
. Не пользуясь никакими другими арифметическими операциями, кроме умножения, сложения и вычи- тания, вычислить за минимальное число операций 2
x
4
– 4
x
3
+ 2
x
– 1.
2.
Ввести любой символ и определить его порядковый номер, а также указать предыдущий и последующий символы.
3.
Дана длина ребра куба. Найти площадь грани, площадь полной поверхности и объем этого куба.
4.
Треугольник задан величинами своих углов и радиусом опи- санной окружности. Найти стороны треугольника.
5.
Дано
a
. Не используя никаких функций и никаких операций, кроме умножения, получить
а
8
за три операции;
а
10
и
а
16
за четыре операции. assert ( х <= 10 ) ;
#define NDEBUG;
0>