Приоритет операций и порядок выполнения (ассоциативность)

Приоритет и ассоциативность операций влияют на порядок группирования операндов и порядок вычислений в выражениях С. Например, приоритет выполнения операций необходимо учитывать при составлении сложных арифметических формул.

Операции, приведенные в одной группе таблицы, имеют одинаковый приоритет и ассоциативность. Порядок убывания приоритета сверху вниз.

Таблица 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 Подготовиться к защите лабораторной работы, изучив вопросы по данной теме.

Наши рекомендации