Какой операции нет в c
Перейти к содержимому

Какой операции нет в c

  • автор:

Какой операции нет в c

В C# используется большинство операций, которые применяются и в других языках программирования. Операции представляют определенные действия над операндами — участниками операции. В качестве операнда может выступать переменной или какое-либо значение (например, число). Операции бывают унарными (выполняются над одним операндом), бинарными — над двумя операндами и тернарными — выполняются над тремя операндами. Рассмотрим все виды операций.

Бинарные арифметические операции:

    + Операция сложения двух чисел:

int x = 10; int z = x + 12; // 22
int x = 10; int z = x - 6; // 4
int x = 10; int z = x * 5; // 50
int x = 10; int z = x / 5; // 2 double a = 10; double b = 3; double c = a / b; // 3.33333333

При делении стоит учитывать, что если оба операнда представляют целые числа, то результат также будет округляться до целого числа:

double z = 10 / 4; //результат равен 2

Несмотря на то, что результат операции в итоге помещается в переменную типа double, которая позволяет сохранить дробную часть, но в самой операции участвуют два литерала, которые по умолчанию рассматриваются как объекты int, то есть целые числа, и результат то же будет целочисленный. Для выхода из этой ситуации необходимо определять литералы или переменные, участвующие в операции, именно как типы double или float:

double z = 10.0 / 4.0; //результат равен 2.5
double x = 10.0; double z = x % 4.0; //результат равен 2

Также есть ряд унарных операций, в которых принимает участие один операнд:

    ++ Операция инкремента Инкремент бывает префиксным: ++x — сначала значение переменной x увеличивается на 1, а потом ее значение возвращается в качестве результата операции.

И также существует постфиксный инкремент: x++ — сначала значение переменной x возвращается в качестве результата операции, а затем к нему прибавляется 1.

int x1 = 5; int z1 = ++x1; // z1=6; x1=6 Console.WriteLine($" - "); int x2 = 5; int z2 = x2++; // z2=5; x2=6 Console.WriteLine($" - ");
int x1 = 5; int z1 = --x1; // z1=4; x1=4 Console.WriteLine($" - "); int x2 = 5; int z2 = x2--; // z2=5; x2=4 Console.WriteLine($" - ");

При выполнении сразу нескольких арифметических операций следует учитывать порядок их выполнения. Приоритет операций от наивысшего к низшему:

  1. Инкремент, декремент
  2. Умножение, деление, получение остатка
  3. Сложение, вычитание

Для изменения порядка следования операций применяются скобки.

Рассмотрим набор операций:

int a = 3; int b = 5; int c = 40; int d = c---b*a; // a=3 b=5 c=39 d=25 Console.WriteLine($"a= b= c= d=");

Здесь мы имеем дело с тремя операциями: декремент, вычитание и умножение. Сначала выполняется декремент переменной c, затем умножение b*a, и в конце вычитание. То есть фактически набор операций выглядел так:

int d = (c--)-(b*a);

Но с помощью скобок мы могли бы изменить порядок операций, например, следующим образом:

int a = 3; int b = 5; int c = 40; int d = (c-(--b))*a; // a=3 b=4 c=40 d=108 Console.WriteLine($"a= b= c= d=");

Ассоциативность операторов

Как выше было отмечено, операции умножения и деления имеют один и тот же приоритет, но какой тогда результат будет в выражении:

int x = 10 / 5 * 2;

Стоит нам трактовать это выражение как (10 / 5) * 2 или как 10 / (5 * 2) ? Ведь в зависимости от трактовки мы получим разные результаты.

Когда операции имеют один и тот же приоритет, порядок вычисления определяется ассоциативностью операторов. В зависимости от ассоциативности есть два типа операторов:

  • Левоассоциативные операторы, которые выполняются слева направо
  • Правоассоциативные операторы, которые выполняются справа налево

Все арифметические операторы являются левоассоциативными, то есть выполняются слева направо. Поэтому выражение 10 / 5 * 2 необходимо трактовать как (10 / 5) * 2 , то есть результатом будет 4.

Операции

Операции в C++, как и в других языках программирования, делятся на два основных типа: унарные и бинарные.

К унарным относятся операции, которые проводятся над одним операндом. Весь список выглядит так

  1. Инкремент и декремент

Добавляют или отнимают из переменной единицу.

  1. Взятие адреса и вызов по адресу переменной (см. указатели и ссылки)

Функционал полностью соответствует названию

  1. Унарные плюс и минус

Соответственно аналогичны умножению переменной с -1 и +1. Унарный минус иногда называют операцией смены знака числа.

  1. Выделение и освобождение памяти

a = new int ;

Выделяет участок памяти под переменную определенного типа

Возвращает выделенную памяти обратно в “кучу”.

  1. Определение размера

int b = sizeof ( a ) ;

Возвращает количество байт переменной

  1. Оператор доступа

Позволяет обратиться к глобальной переменной, минуя локальную переменную с таким же именем

К бинарным относят операции с 2 операндами. Это все арифметические операции, операции сравнения, битовые и разрядные сдвиги, логические и (&&) и или (||) и присваивание.

Есть так же особый вид — тернарная операция, позволяющая работать с 3 знаками.

Операции в языке Си

Над объектами в языке Си могут выполняться различные операции:

  • операции присваивания;
  • операции отношения;
  • арифметические;
  • логические;
  • сдвиговые операции.

Результатом выполнения операции является число.

Операции могут быть бинарными или унарными.
Бинарные операции выполняются над двумя объектами, унарные — над одним.

Операция присваивания

Операция присваивания обозначается символом = и выполняется в 2 этапа:

  • вычисляется выражение в правой части;
  • результат присваивается операнду, стоящему в левой части:

объект = выражение;

int a = 4; // переменной a присваивается значение 4
int b;
b = a + 2; // переменной b присваивается значение 6, вычисленное в правой части

В случае если объекты в левой и правой части операции присваивания имеют разные типы используется операция явного приведения типа.
объект = (тип)выражение;

float a = 241.5;
// Перед вычислением остатка от деления a приводится к целому типу
int b = ( int )a % 2; // b = 1

Операции отношения

Основные операции отношения:

Операции отношения используются при организации условий и ветвлений. Результатом этих операций является 1 бит, значение которого равно 1 , если результат выполнения операции — истина, и равно 0 , если результат выполнения операции — ложь.

Арифметические операции

Основные бинарные операции, расположенные в порядке уменьшения приоритета:

Основные унарные операции:

  • ++ — инкрементирование (увеличение на 1);
  • –– — декрементирование (уменьшение на 1);
  • — изменение знака.

Результат вычисления выражения, содержащего операции инкрементирования или декрементирования, зависит от того, где расположен знак операции (до объекта или после него). Если операция расположена до объекта, то сначала происходит изменение значения переменной на 1, а потом это значение используется для выполнения следующих операций. Если операция ++ или расположена после переменной, то сначала выполняется операция, а потом значение переменной изменяется на 1.

int a=2;
int b=3;
int c;
c = a*++b;
// c=8, поскольку в операции умножения
//уже b=4

int a=2;
int b=3;
int d;
d = a*b++;
// d=6, поскольку в операции умножения b=3,
// следующим действием будет b=4

Бинарные арифметические операции могут быть объединены с операцией присваивания:

  • объект *= выражение; // объект = объект * выражение
  • объект /= выражение; // объект = объект / выражение
  • объект += выражение; // объект = объект + выражение
  • объект -= выражение; // объект = объект — выражение
  • объект %= выражение; // объект = объект % выражение

Логические операции

Логические операции делятся на две группы:

Условные логические операции чаще всего используются в операциях проверки условия if и могут выполняться над любыми объектами. Результат условной логической операции:

  • 1 если выражение истинно;
  • 0 если выражение ложно.

Вообще, все значения, отличные от нуля, интерпретируются условными логическими операциями как истинные.

Основные условные логические операции:

  • && — И (бинарная) — требуется одновременное выполнение всех операций отношения;
  • || — ИЛИ (бинарная) — требуется выполнение хотя бы одной операции отношения;
  • ! — НЕ (унарная) — требуется невыполнение операции отношения.

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

Основные побитовые логические операции в языке Си:

  • & конъюнкция (логическое И) — бинарная операция, результат которой равен 1 только когда оба операнда единичны (в общем случае — когда все операнды единичны);
  • | дизъюнкция (логическое ИЛИ) — бинарная операция, результат которой равен 1 когда хотя бы один из операндов равен 1;
  • ~ инверсия (логическое НЕ) — унарная операция, результат которой равен 0 если операнд единичный, и равен 1, если операнд нулевой;
  • ^ исключающее ИЛИ — бинарная операция, результат которой равен 1, если только один из двух операндов равен 1 (в общем случае если во входном наборе операндов нечетное число единиц).

Для каждого бита результат выполнения операции будет получен в соответствии с таблицей.

a b a & b a | b ~a a ^ b
0 0 0 0 1 0
0 1 0 1 1 1
1 0 0 1 0 1
1 1 1 1 0 0

unsigned char a = 14; // a = 0000 1110
unsigned char b = 9; // b = 0000 1001
unsigned char c, d, e, f;
c = a & b; // c = 8 = 0000 1000
d = a | b; // d = 15 = 0000 1111
e = ~a; // e = 241 = 1111 0001
f = a ^ b; // f = 7 = 0000 0111

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

Бит Маска
0 0x01
1 0x02
2 0x04
3 0x08
4 0x10
5 0x20
6 0x40
7 0x80

Для установки определенного бита необходимо соответствующий бит маски установить в 1 и произвести операцию побитового логического ИЛИ с константой, представляющей собой маску:

unsigned char a = 3;
a = a | 0x04; // a = 7, бит 2 установлен

Для сброса определенного бита необходимо соответствующий бит маски сбросить в 0 и произвести операцию побитового логического И с константой, представляющей собой инверсную маску:

unsigned char a = 3;
a = a & (~0x02); // a = 1, бит 1 сброшен

Бинарные побитовые логические операции могут быть объединены с операцией присваивания:

  • объект &= выражение; // объект = объект & выражение
  • объект |= выражение; // объект = объект | выражение
  • объект ^= выражение; // объект = объект ^ выражение

Сдвиговые операции

Операции арифметического сдвига применяются в целочисленной арифметике и обозначаются как:

Общий синтаксис осуществления операции сдвига:
объект = выражение сдвиг КоличествоРазрядов;

unsigned char a=6; // a = 0000 0110
unsigned char b;
b = a >> 1; // b = 0000 0110 >> 1 = 0000 0011 = 3

Арифметический сдвиг целого числа вправо >> на 1 разряд соответствует делению числа на 2.
Арифметический сдвиг целого числа влево на 1 разряд соответствует умножению числа на 2.

Комментариев к записи: 41

1. Операторы языка C++

Операторы управляют процессом выполнения программы. Набор операторов языка С++ содержит все управляющие конструкции структурного программирования.

    Пустой оператор – ;

Тело оператора for выполняется до тех пор, пока условное выражение не станет ложным (равным 0). Начальное выражение и выражение приращения обычно используются для инициализации и модификации параметров цикла и других значений. Начальное выражение вычисляется один раз до первой проверки условного выражения, а выражение приращения вычисляется после каждого выполнения оператора. Любое из трех выражений заголовка цикла, и даже все три могут быть опущены (не забывайте только оставлять точки с запятой). Если опущено условное выражение, то оно считается истинным, и цикл становится бесконечным.

Оператора возврата заканчивает выполнение функции, в которой он содержится, и возвращает управление в вызывающую функцию. Управление передается в точку вызывающей функции, непосредственно следующую за оператором вызова. Значение выражения, если она задано, вычисляется, приводится к типу, объявленному для функции, содержащей оператор возврата, и возвращается в вызывающую функцию. Если выражение опущено, то возвращаемое функцией значение не определено.

Ввод/вывод не является частью языка С++ , а осуществляется функциями, входящими в состав стандартной библиотеки. Для подробной информации см. лекцию 4.

2. Структура программы

Программа на языке С++ состоит из директив препроцессора, указаний компилятору, объявлений переменных и/или констант, объявлений и определений функций.

2.1. Объявление переменной

Объявление переменной задаёт имя и атрибуты переменной. Атрибутами переменной могут быть тип, количество элементов (для массивов), спецификация класса памяти, а также инициализатор. Инициализатор – это константа соответствующего типа, задающая значение, которое присваивается переменной при создании.

Объявление переменной имеет следующий синтаксис: [ ] [= ] [, [= ] . ];

В языке С++ нет ограничений на количество символов в имени. Однако некоторые части реализации (в частности, компоновщик) недоступны автору компилятора, и они иногда накладывают такие ограничения.

2.1.1. Константы

В языке С++ введена концепция определяемых пользователем констант для указания на то, что значение нельзя изменить непосредственно. Это может быть полезно в нескольких отношениях. Например, многие объекты не меняются после инициализации; использование символических констант приводит к более удобному в сопровождении коду, чем применение литералов непосредственно в тексте программы; указатели часто используются только для чтения, но не для записи; большинство параметров функций читаются, но не перезаписываются.

Типичным является использование констант в качестве размера массивов и меток в инструкции case.

Отметьте, что const модифицирует тип, т.е. ограничивает возможное использование объекта, но не указывает способ размещения константного объекта. Простым и типичным использованием константы является тот случай, когда значение константы известно во время компиляции и под неё не требуется выделение памяти. Для массива констант, как правило, требуется выделение памяти, так как, в общем случае, компилятор не в состоянии определить, к какому элементу массива происходит обращение в выражении.

2.1.2. Объявление typedef

Имена, вводимые с помощью typedef, являются синонимами, а не новыми типами. Следовательно, старые типы можно использовать совместно с их синонимами. Если вам нужны различные типы с одинаковой семантикой или с одинаковым представлением, обратитесь к перечислениям или классам.

2.2. Объявление и определение функции

Объявление функции задаёт имя функции, тип возвращаемого значения и количество и типы параметров, которые должны присутствовать при вызове функции. Указание void в качестве возвращаемого значения означает, что функция не возвращает значения.

Определением функции является объявление функции, в котором присутствует тело функции. Определение функции имеет следующий синтаксис:
( ) [ ] [ ] >

Типы в определении и объявлениях функции должны совпадать. Однако, имена параметров не являются частью типа и не обязаны совпадать.

Все функции в программе существуют на глобальном уровне и не могут быть вложены друг в друга.

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

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

Такая форма инструкции return важна при написании шаблонов функций, когда тип возвращаемого значения является параметром шаблона.

2.2.1. Встраиваемые функции

Открытая подстановка не влияет на результаты вызова функции, чем отличается от макроподстановки. Встраиваемая функция имеет обычный синтаксис описания функции и подчиняется всем правилам, касающимся области видимости и контроля типов. Открытая подстановка является просто иной реализацией вызова функции. Вместо генерации кода, передающего управление и параметры единственному экземпляру тела функции, копия тела функции, соответственно модифицированная, помещается на место вызова. Это экономит время для передачи управления.

Для всех, кроме простейших, функций время выполнения функций доминирует над издержками времени на обслуживание вызова. Из этого следует, что для всех, кроме простейших, функций экономия за счёт открытой подстановки минимальна. Идеальным кандидатом для открытой подстановки является функция, делающая нечто простое, вроде увеличения или возврата значения. Существование таких функций вызывается необходимостью обработки скрытых данных.

2.2.2. Параметры функций по умолчанию

2.2.3. Параметры программы

Первый параметр содержит количество элементов в массиве – втором параметре, который является массивом указателей на строки. Каждая строка хранит один переданный программе параметр, при этом первый параметр (с индексом 0) содержит имя исполняемого файла и существует всегда. Порядок объявления параметров существенен.

2.2.4. Функции с переменным числом параметров

В языке С++ существует возможность использовать функции с переменным числом параметров. Для объявления такой функции надо указать многоточие (,…) в конце списка параметров функции. Для вызова такой функции не требуется никаких специальных действий, просто задается столько параметров, сколько нужно.

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

Функцию только с необъявленными параметрами, в принципе, определить можно, но выбрать параметры будет затруднительно, т.к. макроопределения для работы с необъявленными параметрами используют имя последнего объявленного формального параметра.

Внутри функции программист сам отвечает за выбор из стека дополнительных параметров. Для работы с ними используются макроопределения va_arg , va_start и va_end , определённые в файле stdarg.h .

Пример программы с функцией с перменным числом параметров см. в конце лекции.

2.3. Препроцессор

  • включение в программу текстов из указанных файлов;
  • замена идентификаторов последовательностями символов;
  • макроподстановка, т.е. замена обозначения параметризованным текстом, формируемым препроцессором с учетом конкретных аргументов;
  • исключение из программы отдельных частей текста (условная компиляция).

2.3.1. Включение файлов

Включение файлов производиться с помощью директивы #include, которая имеет следующий синтаксис:
#include #include «путь»

Угловые скобки здесь являются элементом синтаксиса.

Директива #include включает содержимое файла, путь к которому задан, в компилируемый файл вместо строки с директивой. Если путь заключен в угловые скобки, то поиск файла осуществляется в стандартных директориях. Если путь заключен в кавычки и задан полностью, то поиск файла осуществляется в заданной директории, а если путь полностью не задан – в текущей директории. С помощью это директивы Вы можете включать в текст программы как стандартные, так и свои файлы.

Во включаемый файл можно поместить, например, общие для нескольких исходных файлов определения именованных констант и макроопределения. Включаемые файлы используются также для хранения объявлений внешних переменных и абстрактных типов данных, разделяемых несколькими исходными файлами. Более подробную информацию об использовании заголовочных файлов см. в лекции 9.

Кроме того, как было указано выше, в языке С++ ряд функций, такие как функции ввода/вывода, динамического распределения памяти и т.д., не являются элементом языка, а входят в стандартные библиотеки. Для того чтобы пользоваться функциями стандартных библиотек, необходимо в текст программы включать так называемые заголовочные файлы (в описании каждой функции указывается, какой заголовочный файл необходим для неё). Это также делается с помощью директивы препроцессора #include.

Директива #include может быть вложенной. Это значит, что она может встретиться в файле, включенном другой директивой #include. Допустимый уровень вложенности директив #include зависит от реализации компилятора.

2.3.2. Макроподстановки

Макроподстановки реализуются директивой #define, которая имеет следующий синтаксис:
#define #define ( )

Директива #define заменяет все вхождения идентификатора в исходном файле на текст, следующий в директиве за идентификатором. Этот процесс называется макроподстановкой. Идентификатор заменяется лишь в том случае, если он представляет собой отдельную лексему. Например, если идентификатор является частью строки или более длинного идентификатора, он не заменяется.

Текст представляет собой набор лексем, таких как ключевые слова, константы, идентификаторы или выражение. Один или более пробельных символов должны отделять текст от идентификатора (или от заключённых в скобки параметров). Если текст не умещается на строке, то он может быть продолжен на следующей строке, для этого следует набрать в конце строки символ «обратный слэш» и сразу за ним нажать клавишу «ВВОД».

Текст может быть опущен. В этом случае все экземпляры идентификатора будут удалены из исходного текста программы. Тем не менее, сам идентификатор рассматривается как определённый.

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

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

После того как выполнена макроподстановка, полученная строка вновь просматривается для поиска других имен макроопределений. При повторном просмотре не принимается к рассмотрению имя ранее произведенной макроподстановки. Поэтому директива #define x x не приведет к зацикливанию препроцессора.

Вызов MULT(x + y, z) будет заменен на ((x + y) * (z)) . При отсутствии внутренних скобок получилось бы (x + y * z) , что неверно.

Макровызов MAX(i, a[i++]) заменится на ((i) > (a[i++])) ? (i) : (a[i++])) . Результат вычисления непредсказуем.

В директиве #define две лексемы могут быть «склеены» вместе. Для этого их нужно объединить знаками ## (слева и справа допустимы пробельные символы). Препроцессор объединяет такие лексемы в одну. Например, макроопределение #define VAR(i, j) i ## j при макровызове VAR(x, 6) образует идентификатор x6.

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

Замены в тексте можно отменить директивой #undef, которая имеет следующий синтаксис:
#undef идентификатор
>

Директива #undef отменяет действие текущего определения #define для идентификатора. Чтобы отменить макроопределение, достаточно задать его идентификатор. Задание списка параметров не требуется. Не является ошибкой применение директивы #undef к идентификатору, который ранее не был определён или действие которого уже отменено.

Принятая в С/С++ форма макросов является серьезным недостатком языка. Теперь эту форму можно считать устаревшей благодаря наличию более подходящих средств языка, таких как шаблоны, пространства имён, встраиваемые функции и константы. Точно также, широкое использование приведений типа в любом языке сигнализирует о плохом проектировании. Как макросы, так и приведения являются частыми источниками ошибок. Тот факт, что без них можно обойтись, делает программирование на С++ гораздо более безопасным и элегантным.

2.3.3. Условная компиляция

Условная компиляция обеспечивается в языке С++ набором команд, которые, по существу, управляют не компиляцией, а препроцессорной обработкой. Эти директивы позволяют исключить из процесса компиляции какие-либо части исходного файла посредством проверки условий.
#if [ ] [#elif [ ]] . [#else [ ]] #endif

Каждой директиве #if в том же исходном файле должна соответствовать завершающая её директива #endif. Между директивами #if и #endif допускается произвольное количество директив #elif и не более одной директивы #else. Если директива #else присутствует, то между ней и директивой #endif на данном уровне вложенности не должно быть других директив #elif.

Препроцессор выбирает участок текста для обработки на основе вычисления константного выражения, следующего за каждой директивой #if и #elif. Выбирается текст, следующий за константным выражением со значением «истина». Если ни одно ограниченное константное выражение не истинно, то препроцессор выбирает текст, следующий за директивой #else. Если же директива #else отсутствует, то никакой текст не выбирается.

Константное выражение может содержать препроцессорную операцию defined( ). Эта операция возвращает истинное значение, если заданный идентификатор в данный момент определён, в противном случае выражение ложно.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *