Приоритет операций и порядок выполнения (ассоциативность)
Приоритет и ассоциативность операций влияют на порядок группирования операндов и порядок вычислений в выражениях С. Например, приоритет выполнения операций необходимо учитывать при составлении сложных арифметических формул.
Операции, приведенные в одной группе таблицы, имеют одинаковый приоритет и ассоциативность. Порядок убывания приоритета сверху вниз.
Таблица 20 - Приоритет операций
Приоритет | Знак операции | Тип операции | Ассоциативность (порядок выполнения) | |||
() [ ] .–> | Первичные | → слева направо | ||||
– ~ ! * & ++ -- sizeof, приведение типов ( ) | Унарные | ← справа налево | ||||
* / % | Мультипликативные | → | ||||
+ - | Аддитивные | → | ||||
<< >> | Сдвиги | → | ||||
< > <= > = | Отношение | → | ||||
= = ! = | Отношение | → | ||||
& | Поразрядное "и" | → | ||||
^ | Поразрядное исключающее "или" | → | ||||
| | Поразрядное включающее "или" | → | ||||
&& | Логическое "и" | → | ||||
| | | Логическое "или" | → | ||||
? : | Условная (тернарная) | ← | ||||
= | * = | / = | % = | Простое и составное присваивание | ← | |
+ = | – = | << = | >> = | |||
& = | | = | ^ = | ||||
, | Последовательное вычисление | → |
Если несколько операций одного приоритета встречаются в выражении, то они применяются в соответствии с ассоциативностью.
Примеры. а = b&ÆхFF + 5; // вычисляется как а = b&(Æх FF + 5);
b=а + с >> 1; // как b=(а +с) >> 1;
с = а + + + b/5; //какс=(а + +) + ( b/5);
Мультипликативные, аддитивные и поразрядные операции обладают свойством коммутативности. Компилятор вычисляет выражения с учётом приоритета в любом порядке, даже если есть скобки. Определённый порядок вычисления (,) операндов гарантируют операции: последовательного вычисления, логические «И» (&&)и «ИЛИ» (||), условная операция(? :).
Запятые в вызовах функций не являются операциями последовательного вычисления и не обеспечивают гарантий вычисления слева направо. Логические операции вычисляют минимальное число операндов, необходимых для определения результатов выражения.
func (i + 1, i = j + 2); //. Не гарантирует порядок вычисления фактических
// аргументов
i= 0;// iимеет тип int по умолчанию
a [++ i] = i; // порядок вычисления левого и правого операндов не
// гарантируются a [0] = 0 или a[1]=1
(x - 5) && ++ i // Если x =5, то ++ i не вычисляется
int x, y, z, f();
z = x > y || f(x, y); // Если x > y,то значение z = 1 «Истина», а f( ) –не
// вызывается
// если x £ y, то f() вызывается, тогда z=0,
// eсли f( ) возвращает нулевое значение, или z = 1,
// если f( ) возвращает не нулевое значение
// printf (“%d %d \n“, ++n, p()2, n)
//в функцию может передаваться n или n+1.
Преобразование типов
В выражениях С переменные различных типов в ряде случаев могут использоваться совместно; например, переменные типа char могут присутствовать в выражениях одновременно с переменными типа int.
Пример совместного использования целых и символьных переменных.
char ch='a', ans; //объявление переменныхchиans
printf("значение ch + 3 = %d", ch+3); //вывод значенияch+3
ans = ch % 3;// определение остатка от целочисленного деления
printf("\n\n значение ans = % d\n", ans);
Поскольку char это целый тип, для него применимы все операции, операнды которых могут иметь тип int. Целые по умолчанию - это величины со знаком signed.
С переменными вещественного типа (float, double и др.) применимы все операции, допустимые для целого типаint,за исключением операции остатка от деления (%).
Преобразования типов бывают явные и неявные. Синтаксис операции явного преобразования типа
(<новый_тип>) <операнд>
или
<новый_тип> (<операнд>).
Ряд операций может в зависимости от типов своих операндов вызывать неявное преобразование значения операнда из одного типа в другой (преобразование по умолчанию).
Рассмотрим результаты таких преобразований.
Данные типа charили short intмогут использоваться везде, где используется тип int. Во всех случаях значение преобразуется к целому типу
Арифметические операции над числами с плавающей точкой (floatиdouble) по умолчанию выполняются с двойной точностью.
Преобразования целочисленных значений в вещественные (плавающие) выполняется без осложнений, но возможна потеря точности, если для результата не предусмотрено достаточного количества битов. Преобразование значений с плавающей точкой к целочисленному типу машинно-зависимо. Результат неопределен, если значение не попадает в отведенный диапазон.
Если целое без знака (unsigned)используется вместе с простым целым, то простое целое и результат преобразуются в целое без знака.
Подавляющее большинство операций вызывает преобразование и определяет типы результата в соответствии с вышеприведенными правилами. Приведенная ниже схема преобразований реализуется по умолчанию при вычислении выражений С.
Сначала любые операнды типов char, unsigned char или short преобразуются в int, а любые операнды типа float преобразуются в double.
Затем, если какой-либо операнд имеет тип double, то другой преобразуется к типу double и типом результата будет double.
В случае, если какой-либо операнд имеет тип unsigned long, то другой преобразуется к типу unsigned long и это же будет и типом результата.
В случае, если какой-либо операнд имеет тип long, то другой преобразуется к типу long и это же будет типом результата.
В случае, если операнд имеет тип unsigned, то другой операнд преобразуется к типу unsigned, и это будет типом результата.
Объект типа void* (указатель на пустой)может быть объявлен для указания на объекты неизвестного типа.
Преобразование типа такого указателя задаётся с помощью явной операции преобразования типов.
Указатель может преобразовываться в любой из целых типов, достаточно большой для того, чтобы вместить его.
Указатель на один тип может быть преобразован в указатель на другой тип. При этом возможно преобразование указателя в указатель на объект меньшего размера и обратно без изменений.
Например, функция выделения памяти может принимать размер (в байтах) объекта, для которого выделяется память, и возвращать указателю несоответствующий тип, который может таким образом использоваться.
extern void* allos ();
doube* dp;
dp = (doube*) allos (sizeof (doube));
*dp = 2,6/8,4
Пример 17
#include <stdio.h>
#include <conio.h>
#include <math.h>
main()
{float r1,r2;
int a,b,b1;
unsigned c,d;
char e,f;
unsigned char g;
float f1,f2;
clrscr();
printf("ввод первого u второго вещественных чисел: ");
scanf("%f %f",&r1,&r2);
//printf("\n");
printf("вывод результатов операций для чисел: %5.2f %5.2f\n",r1,r2);
printf("!r1= %d ",!r1); printf("!r2= %d ",!r2);
printf("r1>r2 %d ",r1>r2); printf("r1<r2 %d\n",r1<r2);
printf("r1||r2 %d ",r1||r2); printf("r1&&r2 %d ",r1&&r2);
printf("r1==r2 %d ",r1==r2); printf("r1>=r2 %d\n",r1>=r2);
printf("r1<=r2 %d ",r1<=r2); printf("r1!=r2 %d\n",r1!=r2);
//Вложенный блок, переменные переобъявлены: int r1,r2; float b;
{int r1,r2;
float b;
printf("ввод первого u второго целого числа: ");
scanf("%d %d",&r1,&r2);
// printf("\n");
printf("вывод результатов операций для целых чисел: %2d %2d\n",r1,r2);
printf("!r1= %d ",!r1); printf("!r2= %d ",!r2);
printf("r1>r2 %d ",r1>r2); printf("r1<r2 %d\n" ,r1<r2);
printf("r1||r2 %d ",r1||r2); printf("r1&&r2 %d ",r1&&r2);
printf("r1==r2 %d ",r1==r2); printf("r1>=r2 %d\n" ,r1>=r2);
printf("r1<=r2 %d ",r1<=r2); printf("r1!=r2 %d ",r1!=r2);
printf("~r1 %d ",~r1); printf("r1|r2 %d\n" ,r1|r2);
printf("r1^r2 %d ",r1^r2); printf("r1&r2 %d ",r1&r2);
printf("r1<<r2 %d ",r1<<r2); printf("r1>>r2 %d\n" ,r1>>r2);
printf("Исходные значения: r1=%d r2=%d\n",r1,r2);
r2=r1++; //Постфиксные операции а1++ а1--
printf("r2=r1++; r1=%d r2=%d\n",r1,r2);
--r1; r2=++r1; //Префиксные операции ++а1 --а1
printf("--r1; r2=++r1; r1=%d r2=%d\n",r1,r2);
r1-=4; r2+=5; //Составное присваивание
printf("r1-=4; r2+=5; r1=%d r2=%d\n",r1,r2);
a=r2-=2,r1+=5; //Составное присваивание
printf("a=r2-=2,r1+=5; r1=%d r2=%d a=%d\n",r1,r2,a);
a=(r1<r2)?r1:r2;//Тернарная операция если r1<r2, то а=r1 иначе а=r2
printf("a=(r1<r2)?r1:r2; a=%d\n",a);
a=r2%r1; //Остаток от деления целых
printf("а=r1%r2; "); printf("а=%d\n",r2%r1);
a=r2/r1; //Деление целых
printf("a=r2/r1; a=%d\n",a);
b=(float)r2/(float)r1; //Деление c преобразованием типов
printf("b=(float)r2/(float)r1; b=%f\n",b);
}
float q=1.3,q1=2.4,raz;
printf("Введите переменные a-(int), \
c-(unsigned), g-(unsigned char)\n");
scanf("%i,%u,%uc",&a,&c,&g);
b = (a & (c<<3));
b1 = (a & 3) << 7;
f = (a & 3) << 7;
f1 = q / (c | 0x3E);
f2 = a / (c | 0x3E);
raz=exp(q+q1)/4;
printf("g=%u, q=%5.2f, q1=%7.2f, b=%i, b1=%i, \
\n",g,q,q1,b,b1);
printf("f=%i, f1=%6.3f, f2=%6.3f, raz=%f\n",f,f1,f2,raz);
getch(); return 0;
}
/* ввод первого u второго вещественных чисел: 56 7
вывод результатов операций для чисел: 56.00 7.00
!r1= 0 !r2= 0 r1>r2 1 r1<r2 0
r1||r2 1 r1&&r2 1 r1==r2 0 r1>=r2 1
r1<=r2 0 r1!=r2 1
ввод первого u второго целого числа: 45 2
вывод результатов операций для целых чисел: 45 2
!r1= 0 !r2= 0 r1>r2 1 r1<r2 0
r1||r2 1 r1&&r2 1 r1==r2 0 r1>=r2 1
r1<=r2 0 r1!=r2 1 ~r1 -46 r1|r2 47
r1^r2 47 r1&r2 0 r1<<r2 180 r1>>r2 11
Исходные значения: r1=45 r2=2
r2=r1++; r1=46 r2=45
--r1; r2=++r1; r1=46 r2=46
r1-=4; r2+=5; r1=42 r2=51
a=r2-=2,r1+=5; r1=47 r2=49 a=49
a=(r1<r2)?r1:r2; a=47
а=r1%r2; а=2
a=r2/r1; a=1
b=(float)r2/(float)r1; b=1.042553
Введите переменные a-(int), c-(unsigned), g-(unsigned char)
-34 6 7
g=122, q =1.30, q1=2.40, b=512, b1=256,
f=0, f1=0.010, f2=519.000, raz=10.111827 */
Ход работы
1 Изучить теоретические сведения.
2 Для использования арифметических, логических и других операций, приведенных в таблице задаться выражениями, содержащими указанные операции. В качестве базы принять лабораторную работу №5.
3 Ознакомившись с приоритетом операций, показать порядок выполнения операций в конкретных выражениях с использованием скобок.
4 Для преобразования типов переменных использовать явное и неявное преобразование типов.
5 Разработать алгоритм и программу, отладить ее на компьютере.
6 Изучить выполнение операций и тип результата.
7 Получить результаты и сделать выводы по работе.
8 Оформить отчет.
9 Подготовиться к защите лабораторной работы, изучив вопросы по данной теме.