Логическое или в c. Операторы отношения и логические операторы. Побитовые операции в СИ

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

В С++ существует три логические операции:

  1. Логическая операция И && , нам уже известная;
  2. Логическая операция ИЛИ || ;
  3. Логическая операция НЕ ! или логическое отрицание.

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

Сейчас следует понять разницу между логической операцией И и логической операцией ИЛИ , чтобы в дальнейшем не путаться. Пришло время познакомиться с типом данных bool –логический . Данный тип данных может принимать два значения: true (истина) и false (ложь). Проверяемое условие в операторах выбора имеет тип данных bool . Рассмотрим принцип работы следующей программы, и все будет понятно со всеми этими логическими операциями.

// or_and_not.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; int main(int argc, char* argv) { bool a1 = true, a2 = false; // объявление логических переменных bool a3 = true, a4 = false; cout << "Tablica istinnosti log operacii &&" << endl; cout << "true && false: " << (a1 && a2) << endl // логическое И << "false && true: " << (a2 && a1) << endl << "true && true: " << (a1 && a3) << endl << "false && false: " << (a2 && a4) << endl; cout << "Tablica istinnosti log operacii ||" << endl; cout << "true || false: " << (a1 || a2) << endl // логическое ИЛИ << "false || true: " << (a2 || a1) << endl << "true || true: " << (a1 || a3) << endl << "false || false: " << (a2 || a4) << endl; cout << "Tablica istinnosti log operacii !" << endl; cout << "!true: " << (! a1) << endl // логическое НЕ << "!false: "<< (! a2) << endl; system("pause"); return 0; }

Строки 9 и 10 вам должны быть понятны, так как здесь инициализируются переменные типа bool . Причем каждой переменной присваивается значение true или false . Начиная с 9-й строки и заканчивая 20-й , показано использование логических операций. Результат работы программы (см. Рисунок 1).

Tablica istinnosti log operacii && true && false: 0 false && true: 0 true && true: 1 false && false: 0 Tablica istinnosti log operacii || true || false: 1 false || true: 1 true || true: 1 false || false: 0 Tablica istinnosti log operacii ! !true: 0 !false: 1 Для продолжения нажмите любую клавишу. . .

Рисунок 1 — Логические операции С++

Наверное, у вас возникает вопрос, «А что это за нолики и единички?». Если есть вопрос, то на него нужно ответить. Отвечаю: «Нолик-это представление логического значения false (ложь), ну а единички – это логическое true (истина)». Коротко поясню некоторые моменты. Составное условие с использованием логического И истинно только в том случае, когда истинны оба простых условия. Во всех остальных случаях составное условие ложно. Составное условие с использованием логического ИЛИ ложно только в том случае, когда ложные оба простых условия. Во всех остальных случаях составное условие истинно. Логическое отрицание НЕ является унарной операцией, и она не комбинирует два условия, в отличие от логических операций И и ИЛИ , которые являются бинарными операциями. Логическое отрицание позволяет перевернуть смысл условия, что в некоторых случаях очень удобно. Условие с логическим отрицанием истинно в том случае, если это же условие ложно без отрицания, и наоборот.

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

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

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

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

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

Операция присваивания обозначается символом = и выполняется в 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.

Пример :

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

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

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

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

  • условные;
  • побитовые.

Условные логические операции чаще всего используются в операциях проверки условия 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

Пример :

1
2
3
4
5
6
7

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

Примечание. Все операции в результате дают значение типа bool

Операции сравнения и логические операции в результате дают значение типа bool, то есть true или false. Если же такое выражение встречается в контексте, требующем целого значения, true преобразуется в 1, а false – в 0. Вот фрагмент кода, подсчитывающего количество элементов вектора, меньших некоторого заданного значения:

Vector::iterator iter = ivec.beg-in() ; while (iter != ivec.end()) { // эквивалентно: e1em_cnt = e1em_cnt + (*iter < some_va1ue) // значение true/false выражения *iter < some_va1ue // превращается в 1 или 0 e1em_cnt += *iter < some_va1ue; ++iter; }

Мы просто прибавляем результат операции “меньше” к счетчику. (Пара += обозначает составной оператор присваивания, который складывает операнд, стоящий слева, и операнд, стоящий справа. То же самое можно записать более компактно: elem_count = elem_count + n. Мы рассмотрим такие операторы в разделе 4.4.)
Логическое И (&&) возвращает истину только тогда, когда истинны оба операнда. Логическое ИЛИ (||) дает истину, если истинен хотя бы один из операндов. Гарантируется, что операнды вычисляются слева направо и вычисление заканчивается, как только результирующее значение становится известно. Что это значит? Пусть даны два выражения:

Expr1 && expr2 expr1 || expr2

Если в первом из них expr1 равно false, значение всего выражения тоже будет равным false вне зависимости от значения expr2, которое даже не будет вычисляться. Во втором выражении expr2 не оценивается, если expr1 равно true, поскольку значение всего выражения равно true вне зависимости от expr2.
Подобный способ вычисления дает возможность удобной проверки нескольких выражений в одном операторе AND:

While (ptr != О && ptr->va1ue < upperBound && ptr->va1ue >= 0 && notFound(ia[ ptr->va1ue ])) { ... }

Указатель с нулевым значением не указывает ни на какой объект, поэтому применение к нулевому указателю операции доступа к члену вызвало бы ошибку (ptr->value). Однако, если ptr равен 0, проверка на первом шаге прекращает дальнейшее вычисление подвыражений. Аналогично на втором и третьем шагах проверяется попадание величины ptr->value в нужный диапазон, и операция взятия индекса не применяется к массиву ia, если этот индекс неправилен.
Операция логического НЕ дает true, если ее единственный оператор равен false, и наоборот. Например:

Bool found = false; // пока элемент не найден // и ptr указывает на объект (не 0) while (! found && ptr) { found = 1ookup(*ptr); ++ptr; }

Подвыражение

Дает true, если переменная found равна false. Это более компактная запись для

Found == false

Аналогично

Эквивалентно более длинной записи

If (found == true)

Использование операций сравнения достаточно очевидно. Нужно только иметь в виду, что, в отличие от И и ИЛИ, порядок вычисления операндов таких выражений не определен. Вот пример, где возможна подобная ошибка:

// Внимание! Порядок вычислений не определен! if (ia[ index++ ] < ia[ index ]) // поменять местами элементы

Программист предполагал, что левый операнд оценивается первым и сравниваться будут элементы ia и ia. Однако компилятор не гарантирует вычислений слева направо, и в таком случае элемент ia может быть сравнен сам с собой. Гораздо лучше написать более понятный и машинно-независимый код:

If (ia[ index ] < ia[ index+1 ]) // поменять местами элементы ++index;

Еще один пример возможной ошибки. Мы хотели убедиться, что все три величины ival, jval и kval различаются. Где мы промахнулись?

// Внимание! это не сравнение 3 переменных друг с другом if (ival != jva1 != kva1) // do something ...

Значения 0, 1 и 0 дают в результате вычисления такого выражения true. Почему? Сначала проверяется ival != jval, а потом итог этой проверки (true/false – преобразованной к 1/0) сравнивается с kval. Мы должны были явно написать:
if (ival != jva1 && ival != kva1 && jva1 != kva1)
// сделать что-то...

Упражнение 4.4

Найдите неправильные или непереносимые выражения, поясните. Как их можно изменить? (Заметим, что типы объектов не играют роли в данных примерах.)
(a) ptr->iva1 != 0
(с) ptr != 0 && *ptr++
(e) vec[ iva1++ ] <= vec[ ival ];
(b) ival != jva1 < kva1 (d) iva1++ && ival

Упражнение 4.5

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

В тексте на любом естественном языке можно выделить четыре основных элемента: символы, слова, словосочетания и предложения. Алгоритмический язык также содержит такие элементы, только слова называют лексемами (элементарными конструкциями), словосочетания – выражениями, предложения – операторами. Лексемы образуются из символов, выражения из лексем и символов, операторы из символов выражений и лексем (Рис. 1.1)

Рис. 1.1. Состав алгоритмического языка

Таким образом, элементами алгоритмического языка являются:

1) Алфавит языка СИ++, который включает

— прописные и строчные латинские буквы и знак подчеркивания;

— арабские цифры от 0 до 9;

— специальные знаки “{},| ()+-/%*.\’:;&?<>=!#^

— пробельные символы (пробел, символ табуляции, символы перехода на новую строку).

2) Из символов формируются лексемы языка:

Идентификаторы – имена объектов СИ-программ. В идентификаторе могут быть использованы латинские буквы, цифры и знак подчеркивания. Прописные и строчные буквы различаются, например, PROG1, prog1 и Prog1 – три различных идентификатора. Первым символом должна быть буква или знак подчеркивания (но не цифра). Пробелы в идентификаторах не допускаются.

Ключевые (зарезервированные) слова – это слова, которые имеют специальное значение для компилятора. Их нельзя использовать в качестве идентификаторов.

— Знаки операций – это один или несколько символов, определяющих действие над операндами. Операции делятся на унарные, бинарные и тернарную по количеству участвующих в этой операции операндов.

Константы – это неизменяемые величины. Существуют целые, вещественные, символьные и строковые константы. Компилятор выделяет константу в качестве лексемы (элементарной конструкции) и относит ее к одному из типов по ее внешнему виду.

Разделители – скобки, точка, запятая пробельные символы.

Константы в Си++

Константа – это лексема, представляющая изображение фиксированного числового, строкового или символьного значения.

Константы делятся на 5 групп:

— вещественные (с плавающей точкой);

— перечислимые;

— символьные;

— строковые.

Компилятор выделяет лексему и относит ее к той или другой группе, а затем внутри группы к определенному типу по ее форме записи в тексте программы и по числовому значению.

Целые константы могут быть десятичными, восьмеричными и шестнадцатеричными. Десятичная константа определяется как последовательность десятичных цифр, начинающаяся не с 0, если это число не 0 (примеры: 8, 0, 192345). Восьмеричная константа – это константа, которая всегда начинается с 0. За 0 следуют восьмеричные цифры (примеры: 016 – десятичное значение 14, 01). Шестнадцатеричные константы – последовательность шестнадцатеричных цифр, которым предшествуют символы 0х или 0Х (примеры: 0хА, 0Х00F).

В зависимости от значения целой константы компилятор по-разному представит ее в памяти компьютера (т. е. компилятор припишет константе соответствующий тип данных).

Вещественные константы имеют другую форму внутреннего представления в памяти компьютера. Компилятор распознает такие константы по их виду. Вещественные константы могут иметь две формы представления: с фиксированной точкой и с плавающей точкой. Вид константы с фиксированной точкой:[цифры].[цифры] (примеры: 5.7, .0001, 41.).Вид константы с плавающей точкой: [цифры][.][цифры]E|e[+|-][цифры] (примеры:0.5е5, .11е-5, 5Е3). В записи вещественных констант может опускаться либо целая, либо дробная части, либо десятичная точка, либо признак экспоненты с показателем степени.

Перечислимые константы вводятся с помощью ключевого слова enum. Это обычные целые константы, которым приписаны уникальны и удобные для использования обозначения. Примеры: enum { one=1, two=2, three=3,four=4};

enum {zero,one,two,three} – если в определении перечислимых констант опустить знаки = и числовые значения, то значения будут приписываться по умолчанию. При этом самый левый идентификатор получит значение 0, а каждый последующий будет увеличиваться на 1.

enum { ten=10, three=3, four, five, six};

enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday} ;

Символьные константы – это один или два символа, заключенные в апострофы. Символьные константы, состоящие из одного символа, имеют тип char и занимают в памяти один байт, символьные константы, состоящие из двух символов, имеют тип int и занимают два байта. Последовательности, начинающиеся со знака \ , называются управляющими, они используются:

— Для представления символов, не имеющих графического отображения, например:

\a – звуковой сигнал,

\b – возврат на один шаг,

\n – перевод строки,

\t – горизонтальная табуляция.

— Для представления символов: \ , ’ , ? , ” (\\, \’ ,\? ,\”).

— Для представления символов с помощью шестнадцатеричных или восьмеричных кодов (\073, \0хF5).

Строковая константа – это последовательность символов, заключенная в кавычки. Внутри строк также могут использоваться управляющие символы. Например: “\nНовая строка”,

“\n\”Алгоритмические языки программирования высокого уровня \”” .

Типы данных в Си++

Данные отображают в программе окружающий мир. Цель программы состоит в обработке данных. Данные различных типов хранятся и обрабатываются по-разному. Тип данных определяет:

1) внутреннее представление данных в памяти компьютера;

2) множество значений, которые могут принимать величины этого типа;

3) операции и функции, которые можно применять к данным этого типа.

В зависимости от требований задания программист выбирает тип для объектов программы. Типы Си++ можно разделить на простые и составные. К простым типам относят типы, которые характеризуются одним значением. В Си++ определено 6 простых типов данных:

Существует 4 спецификатора типа, уточняющих внутреннее представление и диапазон стандартных типов

short (короткий)

long (длинный)

signed (знаковый)

unsigned (беззнаковый)

Тип int

Значениями этого типа являются целые числа.

Размер типа int не определяется стандартом, а зависит от компьютера и компилятора. Для 16-разрядного процессора под него отводится 2 байта, для 32-разрядного – 4 байта.

Если перед int стоит спецификатор short, то под число отводится 2 байта, а если спецификатор long, то 4 байта. От количества отводимой под объект памяти зависит множество допустимых значений, которые может принимать объект:

short int — занимает 2 байта, следовательно, имеет диапазон –32768 ..+32767;

long int – занимает 4 байта, следовательно, имеет диапазон –2 147 483 648..+2 147 483 647

Тип int совпадает с типом short int на 16-разрядных ПК и с типом long int на 32-разрядных ПК.

Модификаторы signed и unsigned также влияют на множество допустимых значений, которые может принимать объект:

unsigned short int — занимает 2 байта, следовательно, имеет диапазон 0 ..65536;

unsigned long int – занимает 4 байта, следовательно, имеет диапазон 0..+4 294 967 295.

Тип char

Значениями этого типа являются элементы конечного упорядоченного множества символов. Каждому символу ставится в соответствие число, которое называется кодом символа. Под величину символьного типа отводится 1 байт. Тип char может использоваться со спецификаторами signed и unsigned. В данных типа signed char можно хранить значения в диапазоне от –128 до 127. При использовании типа unsigned char значения могут находиться в диапазоне от 0 до 255. Для кодировки используется код ASCII(American Standard Code foe International Interchange). Символы с кодами от 0 до 31 относятся к служебным и имеют самостоятельное значение только в операторах ввода-вывода.

Величины типа char также применяются для хранения чисел из указанных диапазонов.

Тип wchar_ t

Предназначен для работы с набором символов, для кодировки которых недостаточно 1 байта, например Unicode. Размер этого типа, как правило, соответствует типу short. Строковые константы такого типа записываются с префиксом L: L“String #1”.

Тип bool

Тип bool называется логическим. Его величины могут принимать значения true и false. Внутренняя форма представления false – 0, любое другое значение интерпретируется как true.

Типы с плавающей точкой.

Внутреннее представление вещественного числа состоит из 2 частей: мантиссы и порядка. В IBM-совместимых ПК величины типа float занимают 4 байта, из которых один разряд отводится под знак мантиссы, 8 разрядов под порядок и 24 – под мантиссу.

Величины типы double занимают 8 байтов, под порядок и мантиссу отводятся 11 и 52 разряда соответственно. Длина мантиссы определяет точность числа, а длина порядка его диапазон.

Если перед именем типа double стоит спецификатор long, то под величину отводится байтов.

Тип void

К основным типам также относится тип void Множество значений этого типа – пусто.

Переменные

Переменная в СИ++ — именованная область памяти, в которой хранятся данные определенного типа. У переменной есть имя и значение. Имя служит для обращения к области памяти, в которой хранится значение. Перед использованием любая переменная должна быть описана. Примеры:

Общий вид оператора описания:

[класс памяти]тип имя [инициализатор];

Класс памяти может принимать значения: auto, extern, static, register. Класс памяти определяет время жизни и область видимости переменной. Если класс памяти не указан явно, то компилятор определяет его исходя из контекста объявления. Время жизни может быть постоянным – в течение выполнения программы или временным – в течение блока. Область видимости – часть текста программы, из которой допустим обычный доступ к переменной. Обычно область видимости совпадает с областью действия. Кроме того случая, когда во внутреннем блоке существует переменная с таким же именем.

Const – показывает, что эту переменную нельзя изменять (именованная константа).

При описании можно присвоить переменной начальное значение (инициализация).

Классы памяти:

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

extern – глобальная переменная, она находится в другом месте программы (в другом файле или долее по тексту). Используется для создания переменных, которые доступны во всех файлах программы.

static – статическая переменная, она существует только в пределах того файла, где определена переменная.

register — аналогичны auto, но память под них выделяется в регистрах процессора. Если такой возможности нет, то переменные обрабатываются как auto.

Int a; //глобальная переменная void main(){ int b;//локальная переменная extern int x;//переменная х определена в другом месте static int c;//локальная статическая переменная a=1;//присваивание глобальной переменной int a;//локальная переменная а a=2;//присваивание локальной переменной::a=3;//присваивание глобальной переменной } int x=4;//определение и инициализация х

В примере переменная а определена вне всех блоков. Областью действия переменной а является вся программа, кроме тех строк, где используется локальная переменная а. Переменные b и с – локальные, область их видимости – блок. Время жизни различно: память под b выделяется при входе в блок (т. к. по умолчанию класс памяти auto), освобождается при выходе из него. Переменная с (static) существует, пока работает программа.

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

Имя переменной должно быть уникальным в своей области действия.

Описание переменной может быть выполнено или как объявление, или как определение. Объявление содержит информацию о классе памяти и типе переменной, определение вместе с этой информацией дает указание выделить память. В примере extern int x; — объявление, а остальные – определения.

Знаки операций в Си++

Знаки операций обеспечивают формирование выражений. Выражения состоят из операндов, знаков операций и скобок. Каждый операнд является, в свою очередь, выражением или частным случаем выражения – константой или переменной.

Унарные операции

& получение адреса операнда
* Обращение по адресу (разыменование)
унарный минус, меняет знак арифметического операнда
~ поразрядное инвертирование внутреннего двоичного кода целочисленного операнда (побитовое отрицание)
! логическое отрицание (НЕ). В качестве логических значений используется 0 — ложь и не 0 — истина, отрицанием 0 будет 1, отрицанием любого ненулевого числа будет 0.
++ Увеличение на единицу:

префиксная операция — увеличивает операнд до его использования,

постфиксная операция увеличивает операнд после его использования.

int a=(m++)+n; // a=4,m=2,n=2

int b=m+(++n);//a=3,m=1,n=3

— — уменьшение на единицу:

префиксная операция — уменьшает операнд до его использования,

постфиксная операция уменьшает операнд после его использования.

sizeof вычисление размера (в байтах) для объекта того типа, который имеет операнд

имеет две формы

sizeof выражение

sizeof(float)//4

sizeof(1.0)//8, т. к. вещественные константы по умолчанию имеют тип double

Бинарные операции.

Аддитивные:

Мультипликативные:

Операции сдвига (определены только для целочисленных операндов).

Формат выражения с операцией сдвига:

операнд_левый операция_сдвига операнд_правый

Поразрядные операции:

Операции сравнения: результатом являются true(не 0) или false(0)

Логические бинарные операции:

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

И т.д.

Формат операции простого присваивания:

операнд1=операнд2

Леводопустимое значение (L-значение) – выражение, которое адресует некоторый участок памяти, т. е. в него можно занести значение. Это название произошло от операции присваивания, т. к. именно левая часть операции присваивания определяет, в какую область памяти будет занесен результат операции. Переменная – это частный случай леводопустимого выражения.

Условная операция.

В отличие от унарных и бинарных операций в ней используется три операнда.

Выражение1 ? Выражение2: Выражение3;

Первым вычисляется значение выражения1. Если оно истинно, то вычисляется значение выражения2, которое становится результатом. Если при вычислении выражения1 получится 0, то в качестве результата берется значение выражения3.

Например:

x<0 ? -x: x ; //вычисляется абсолютное значение x.

Операция явного (преобразования) приведения типа.

Существует две формы: каноническая и функциональная:

1) (имя_типа) операнд

2) имя_типа (операнд)

(int)a //каноническая форма

int(a) //функциональная форма

Выражения

Из констант, переменных, разделителей и знаков операций можно конструировать выражения. Каждое выражение представляет собой правило вычисления нового значения.. Если выражение формирует целое или вещественное число, то оно называется арифметическим. Пара арифметических выражений, объединенная операцией сравнения, называется отношением. Если отношение имеет ненулевое значение, то оно – истинно, иначе – ложно.

Приоритеты операций в выражениях

Ранг Операции
1 () -> .
2 ! ~ — ++ — & * (тип) sizeof тип()
3 * / % (мультипликативные бинарные)
+ — (аддитивные бинарные)
5 << >> (поразрядного сдвига)
6 < > <= >= (отношения)
7 == != (отношения)
8 & (поразрядная конъюнкция «И»)
9 ^ (поразрядное исключающее «ИЛИ»)
10 | (поразрядная дизъюнкция «ИЛИ»)
11 && (конъюнкция «И»)
12 || (дизъюнкция «ИЛИ»)
13 ?: (условная операция)
14 = *= /= %= -= &= ^= |= <<= >>= (операция присваивания)
15 , (операция запятая)

Теги: Си логические операторы, логическое отрицание, логическое НЕ, !, логическое ИЛИ, логическое сложение, OR, логическое умножение, логическое И, AND, порядок выполнения логических операторов

Логические операторы

Л огические операторы – это операторы, которые принимают в качестве аргументов логические значений (ложь или истину) и возвращают логическое значение. Как и обычные операторы, они могут быть одноместными (унарными, т.е. принимать один аргумент), двуместными (бинарные, принимают два аргумента), трёхместными и т.д.

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

#include #include void main() { char boolValue = -71; if (boolValue) { printf("boolValue is true"); } else { printf("boolValue is false"); } _getch(); }

Логические значения обычно порождаются операторами сравнения (==, !=, >, <, >=. <=).

В языке си представлено три логических оператора: И, ИЛИ и НЕ. Начнём с самого простого

Логическое отрицание

О ператор НЕ (NOT) используется для того, чтобы инвертировать значение аргумента. Т.е., если ему передали истину, то он вернёт ложь, если получил ложь в качестве аргумента, то вернёт истину.

Логический оператор НЕ
X NOT X
0 1
1 0

В си отрицание представлено оператором!. Например

#include #include void main() { int i = 0; if (i) { printf("i is true\n"); } if (!i) { printf("i is not true\n"); } if (!!i) { printf("i is not not true\n"); } if (!!!i) { printf("i is not not not true\n"); } _getch(); }

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

Логическое И

О ператор И (AND, логическое умножение) возвращает истину тогда и только тогда, когда оба аргумента являются истиной.


Логический оператор И
X Y X AND Y
0 0 0
0 1 0
1 0 0
1 1 1

В си логическое умножение представлено оператором &&. Например, задача – в кружок военных спейсмаринов допускаются только совершеннолетние граждане мужского пола. То есть, претендентом может стать только тот, для которого одновременно два условия являются истиной

#include void main() { char gender; unsigned int age; printf("Enter gender ("M" or "F")\n"); scanf("%c", &gender); printf("Enter age\n"); scanf("%u", &age); if (gender == "M" && age > 17) { printf("Wellcome"); } else { printf("Go away"); } _getch(); }

Оператор И может применяться последовательно к нескольким аргументам. Для него действует ассоциативный и коммутативный законы. Усовершенствуем программу, будем также вводить рост:

#define _CRT_SECURE_NO_WARNINGS #include #include void main() { char gender; unsigned int age; unsigned int height; printf("Enter gender ("M" or "F")\n"); scanf("%c", &gender); printf("Enter age\n"); scanf("%u", &age); printf("Enter height\n"); scanf("%u", &height); if (gender == "M" && age > 17 && height >= 180) { printf("Wellcome"); } else { printf("Go away"); } _getch(); }

Также условие могло быть записано

(gender == "M" && age > 17) && height >= 180

Gender == "M" && (age > 17 && height >= 180)

(age > 17 && height >= 180) && gender == "M"

Логическое ИЛИ

О ператор логическое ИЛИ (логическое сложение, OR) истинен тогда, когда истиной является хотя бы один его аргумент.


Логический оператор ИЛИ
X Y X OR Y
0 0 0
0 1 1
1 0 1
1 1 1

В си ИЛИ представлен оператором ||. Например, усовершенствуем программу: теперь пол можно вводить как большой, так и маленькой буквой

#define _CRT_SECURE_NO_WARNINGS #include #include void main() { char genderInput; char gender; unsigned int age; unsigned int height; printf("Enter gender ("M" or "F")\n"); scanf("%c", &genderInput); printf("Enter age\n"); scanf("%u", &age); printf("Enter height\n"); scanf("%u", &height); if (genderInput == "M" || genderInput == "m") { gender = 1; } else { gender = 0; } if ((age > 17 && height >= 180) && gender) { printf("Wellcome"); } else { printf("Go away"); } _getch(); }

Как и в случае оператора И, ИЛИ коммутативен и ассоциативен.

Операторы можно перемешивать друг с другом, создавая сложные операторы

#define _CRT_SECURE_NO_WARNINGS #include #include void main() { char gender; unsigned int age; unsigned int height; printf("Enter gender ("M" or "F")\n"); scanf("%c", &gender); printf("Enter age\n"); scanf("%u", &age); printf("Enter height\n"); scanf("%u", &height); if ((age > 17 && height >= 180) && (gender == "M" || gender == "m")) { printf("Wellcome"); } else { printf("Go away"); } _getch(); }

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

Пример: закон де-Моргана. Чтобы сменить И на ИЛИ (или наоборот), необходимо инвертировать значения всех операндов, заменить И на ИЛИ (или ИЛИ на И) и инвертировать конечный результат. В случае с нашим условием

(age > 17 && height >= 180) && (gender == "M" || gender == "m")

Рассмотрим сначала кусок

(age > 17 && height >= 180)

Меняем все значения на обратные

(!(age > 17) && !(height >= 180))

заменяем оператор && на ||

(!(age > 17) || !(height >= 180))

и инвертируем ответ

!(!(age > 17) || !(height >= 180))

Как видим, результат тот же. Очевидно, что

!(age > 17)

эквивалентно

Age <= 17

Таким образом, изменим условие

!(age <= 17 || height < 180)

Поменяем таким же образом вторую скобку

(gender == "M" || gender == "m")

!(gender != "M" && gender != "m")

!(age <= 17 || height < 180) && !(gender != "M" && gender != "m")

Теперь можно применить это же правило и для всего выражения

!((age <= 17 || height < 180) || (gender != "M" && gender != "m"))

Порядок выполнения логических операторов

Р ассмотрим выражение

A && b && c && d

где a, b, c, d – логические значения. Всё выражение равно истине тогда и только тогда, когда все операнды истинны. Если хотя бы один из операндов ложь, то остальные уже не важны. Поэтому, для оптимизации работы, вычисление происходит слева направо и останавливается, как только был найден первый операнд, равный нулю.

В си оператор присваивания может возвращать значение. Иногда он используется непосредственно в условии:

#define _CRT_SECURE_NO_WARNINGS #include #include #include void main() { int a = 0; int *p = &a; if (a && (p = (int*) malloc(sizeof(int) * 2))) { printf("memory was allocated"); } free(p); _getch(); }

В данном случае, оператор malloc не будет выполнен, так как первый операнд a равен 0 (соответственно, всё выражение равно нулю). Таким образом, оператор free попытается очистить память, которую не может очистить (т.к. p продолжит ссылаться на a). Если же мы поменяем a = 1, то всё отработает без проблем.

То же самое происходит и при выполнение ||. Выражение

A || b || c || d

выполняется слева направо до тех пор, пока не встретит первое ненулевое значение. После этого выполнение останавливается, так как известно, что всё выражение равно истине.

Вверх