23-10-2023
Языки программирования Си и Си++ поддерживают сходный набор операторов. Некоторые из операторов недоступны в языке Си. Кроме того Си не поддерживает перегрузку операторов.
Операторы &&
, ||
и ,
(оператор comma), при условии что не перегружены, определяют sequence point (точку следования) после вычисления первого операнда.
Также в Си++ имеются операторы приведения типа: const_cast
, static_cast
, dynamic_cast
и reinterpret_cast
, которые не были включены в таблицу для краткости.
Многие операторы языков Си и Си++ также доступны в других языках, например C#, Java, Perl, PHP. При этом обычно сохраняется их приоритет, ассоциативность и семантика.
Символами a
, b
и c
обозначаются значения (литералы, значения переменных, возвращаемые значения), имена объектов, lvalue.
«Перегружаемый» обозначает возможность перегрузки оператора в языке C++. «Реализован в Си» означает, существует ли такой оператор в языке Си.
В колонке «пример» показано, как объявлять перегруженный оператор.
Оператор | Синтаксис | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Присваивание | a = b |
Да | Да | R T::operator =(S b); | Н/Д | |
Сложение | a + b |
Да | Да | R T::operator +(S b); | R operator +(S a, T b); | |
Вычитание | a - b |
Да | Да | R T::operator -(S b); | R operator -(S a, T b); | |
Унарный плюс | +a |
Да | Да | R T::operator +(); | R operator +(S a); | |
Унарный минус | -a |
Да | Да | R T::operator -(); | R operator -(S a); | |
Умножение | a * b |
Да | Да | R T::operator *(S b); | R operator *(S a, T b); | |
Деление | a / b |
Да | Да | R T::operator /(S b); | R operator /(S a, T b); | |
Операция модуль (остаток от целочисленного деления)[note 1] | a % b |
Да | Да | R T::operator %(S b); | R operator %(S a, T b); | |
Инкремент | префиксный | ++a |
Да | Да | R T::operator ++(); | R operator ++(S a); |
суффиксный | a++ |
Да | Да | R T::operator ++(int); | R operator ++(S a, int); | |
Прим.: C++ использует неименованный параметр int для различения префиксной и суффиксной форм. | ||||||
Декремент | префиксный | --a |
Да | Да | R T::operator --(); | R operator --(S a); |
суффиксный | a-- |
Да | Да | R T::operator --(int); | R operator --(S a, int); | |
Прим.: C++ использует неименованный параметр int для различения префиксной и суффиксной форм. |
Оператор name | Синтаксис | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Равенство | a == b |
Да | Да | R T::operator ==(S b); | R operator ==(S a, T b); | |
Неравенство | a != b |
Да | Да | R T::operator !=(S b); | R operator !=(S a, T b); | |
Больше | a > b |
Да | Да | R T::operator >(S b); | R operator >(S a, T b); | |
Меньше | a < b |
Да | Да | R T::operator <(S b); | R operator <(S a, T b); | |
Больше или равно | a >= b |
Да | Да | R T::operator >=(S b); | R operator >=(S a, T b); | |
Меньше или равно | a <= b |
Да | Да | R T::operator <=(S b); | R operator <=(S a, T b); |
Оператор | Синтаксис | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Логическое отрицание NOT | !a |
Да | Да | R T::operator !(); | R operator !(S a); | |
Логическое И | a && b |
Да | Да | R T::operator &&(S b); | R operator &&(S a, T b); | |
Логическое ИЛИ | a || b |
Да | Да | R T::operator ||(S b); | R operator ||(S a, T b); |
Оператор | Синтаксис | Перегружаемый | Реализован в Си |
Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Побитовая инверсия | ~a |
Да | Да | R T::operator ~(); | R operator ~(S a); | |
Побитовое И | a & b |
Да | Да | R T::operator &(S b); | R operator &(S a, T b); | |
Побитовое ИЛИ | a | b |
Да | Да | R T::operator |(S b); | R operator |(S a, T b); | |
Побитовое исключающее ИЛИ (XOR) | a ^ b |
Да | Да | R T::operator ^(S b); | R operator ^(S a, T b); | |
Побитовый левый сдвиг[note 2] | a << b |
Да | Да | R T::operator <<(S b); | R operator <<(S a, T b); | |
Побитовый правый сдвиг[note 2][note 3] | a >> b |
Да | Да | R T::operator >>(S b); | R operator >>(S a, T b); |
Оператор | Синтаксис | Значение | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|---|
Член типа T | Определение вне класса | ||||||
Присваивание с суммированием | a += b |
a = a + b |
Да | Да | R T::operator +=(S b); | R operator +=(S a, T b); | |
Присваивание с вычитанием | a -= b |
a = a - b |
Да | Да | R T::operator -=(S b); | R operator -=(S a, T b); | |
Присваивание с умножением | a *= b |
a = a * b |
Да | Да | R T::operator *=(S b); | R operator *=(S a, T b); | |
Присваивание с делением | a /= b |
a = a / b |
Да | Да | R T::operator /=(S b); | R operator /=(S a, T b); | |
Присваивание по модулю | a %= b |
a = a % b |
Да | Да | R T::operator %=(S b); | R operator %=(S a, T b); | |
Присваивание с побитовым И | a &= b |
a = a & b |
Да | Да | R T::operator &=(S b); | R operator &=(S a, T b); | |
Присваивание с побитовым ИЛИ | a |= b |
a = a | b |
Да | Да | R T::operator |=(S b); | R operator |=(S a, T b); | |
Присваивание с побитовым исключающим ИЛИ (XOR) | a ^= b |
a = a ^ b |
Да | Да | R T::operator ^=(S b); | R operator ^=(S a, T b); | |
Присваивание с побитовым сдвигом влево | a <<= b |
a = a << b |
Да | Да | R T::operator <<=(S b); | R operator <<=(S a, T b); | |
Присваивание с побитовым сдвигом вправо[note 3] | a >>= b |
a = a >> b |
Да | Да | R T::operator >>=(S b); | R operator >>=(S a, T b); |
Оператор | Синтакисис | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Обращение к элементу массива | a[b] |
Да | Да | R T::operator [](S b); |
Н/Д | |
Непрямое обращение («объект на который указывает a») | *a |
Да | Да | R T::operator *(); | R operator *(S a); | |
Ссылка («адрес a») | &a |
Да | Да | R T::operator &(); | R operator &(S a); | |
Обращение к члену структуры («член b объекта, на который указывает a») | a->b |
Да | Да | R T::operator ->();[note 4] |
Н/Д | |
Обращение к члену структуры («член b объекта a») | a.b |
Нет | Да | Н/Д | ||
Член, на который указывает b, в объекте на который указывает a[note 5] | a->*b |
Да | Нет | R T::operator ->*(S a); | R operator ->*(S a, T b); | |
Член, на который указывает b, в объекте a | a.*b |
Нет | Нет | Н/Д |
Оператор | Синтаксис | Перегружаемый | Реализован в Си | Пример (R, S и T обозначают тип) | ||
---|---|---|---|---|---|---|
Член типа T | Определение вне класса | |||||
Вызов функции См. Function object. |
a(a1, a2) |
Да | Да | R T::operator ()(S a1, U a2, ...); | Н/Д | |
Оператор "запятая" | a, b |
Да | Да | R T::operator ,(S b); | R operator ,(S a, T b); | |
Условный оператор | a ? b : c |
Нет | Да | Н/Д | ||
Оператор изменения области видимости | a::b |
Нет | Нет | Н/Д | ||
Size-of (размер) | sizeof(a) [note 6]sizeof(type) |
Нет | Да | Н/Д | ||
Align-of (выравнивание) | alignof(type) or _Alignof(type) [note 7] |
Нет | Да | Н/Д | ||
Идентификация типа | typeid(a) typeid(type) |
Нет | Нет | Н/Д | ||
Преобразование типа | (type) a |
Да | Да | T::operator R(); | Н/Д | |
Для определенных пользователем преобразований возвращаемый тип задается неявно и совпадает с именем оператора. | ||||||
Выделение памяти | new type |
Да | Нет | void* T::operator new(size_t x); | void* operator new(size_t x); | |
Выделение памяти (массив) | new type[n] |
Да | Нет | void* T::operator new[](size_t x); | void* operator new[](size_t x); | |
Освобождение памяти | delete a |
Да | Нет | void T::operator delete(void* x); | void operator delete(void* x); | |
Освобождение памяти (массив) | delete[] a |
Да | Нет | void T::operator delete[](void* x); | void operator delete[](void* x); |
Примечания:
alignof
, тогда как в языке C аналогичный оператор называется _Alignof
.В данной таблице указаны приоритеты операторов и их ассоциативность. Операторы, указанные в таблице выше (раньше), имеют более высокий приоритет (приоритет вычисления). При рассмотрении выражения, операторы, имеющие более высокий приоритет, будут вычислены раньше операторов с низким приоритетом. Если несколько операторов указаны в одной ячейке, то они имеют одинаковый приоритет и вычисляются в последовательности, задаваемой ассоциативностью. Приоритеты операторов не изменяются при их перегрузке.
Таблица приоритетов применима в большинстве случаев, но не позволяет решить несколько типов выражений. В частности, условный оператор ?: может содержать в качестве среднего операнда любое выражение. Так, код a ? b , c : d
воспринимается как a ? (b, c) : d
, но не как бессмысленный (a ? b), (c : d)
.
Приоритет | Оператор | Описание | Ассоциативность |
---|---|---|---|
1
Наивысший |
:: |
Изменение области видимости | Слева направо |
2 | ++ |
Суффиксный инкремент | |
-- |
Суффиксный декремент | ||
() |
Вызов функции | ||
[] |
Взятие элемента массива | ||
. |
Выбор элемента по ссылке | ||
-> |
Выбор элемента по указателю | ||
typeid() |
RTTI (только C++; см typeid) | ||
const_cast |
Преобразование типов (C++) (см const cast) | ||
dynamic_cast |
Преобразование типов (C++) (см dynamic cast) | ||
reinterpret_cast |
Преобразование типов (C++) (см reinterpret cast) | ||
static_cast |
Преобразование типов (C++) (см static cast) | ||
3 | ++ |
Префиксный инкремент | Справа налево |
-- |
префиксный декремент | ||
+ |
Унарный плюс | ||
- |
Унарный минус | ||
! |
Логическое НЕ | ||
~ |
Побитовое НЕ | ||
(type) |
Приведение типов | ||
* |
Разыменование указателя | ||
& |
Взятие адреса | ||
sizeof |
Size-of (размер) | ||
new , new[] |
Выделение динамической памяти (C++) | ||
delete , delete[] |
Освобождение динамической памяти (C++) | ||
4 | .* |
Указатель на член (C++) | Слева направо |
->* |
Указатель на член (C++) | ||
5 | * |
Умножение | |
/ |
Деление | ||
% |
Взятие модуля (остаток от деления) | ||
6 | + |
Сложение | |
- |
Вычитание | ||
7 | << |
Побитовый сдвиг влево | |
>> |
Побитовый сдвиг вправо | ||
8 | < |
Менее | |
<= |
Менее или равно | ||
> |
Более | ||
>= |
Более или равно | ||
9 | == |
Равенство | |
!= |
Неравенство | ||
10 | & |
Побитовое И | |
11 | ^ |
Побитовое исключающее или (XOR) | |
12 | | |
Побитовое ИЛИ | |
13 | && |
Логическое И | |
14 | || |
Логическое ИЛИ | |
15 | ?: |
Условный оператор | Справа налево |
16 | = |
Прямое присваивание | Справа налево |
+= |
Присваивание со сложением | ||
-= |
Присваивание с вычитанием | ||
*= |
Присваивание с умножением | ||
/= |
Присваивание с делением | ||
%= |
Присваивание с взятием остатка от деления | ||
<<= |
Присваивание с побитовым сдвигом влево | ||
>>= |
Присваивание с побитовым сдвигом вправо | ||
&= |
Присваивание с побитовым И | ||
^= |
Присваивание с побитовым XOR | ||
|= |
Присваивание с побитовым OR | ||
17 | throw |
Оператор создания исключения (C++) | |
18 | , |
Оператор Comma | Слева направо |
Таблица приоритетов определяет приоритет в цепочках выражений, когда порядок вычислений не был явно задан скобками.
++x*3
был бы двусмысленным без каких-либо правил приоритетов. По таблице можно сказать, что x сначала связывается с оператором ++, и только затем с оператором *, поэтому независимо от действия оператора ++, это действие только над x (а не над x*3
). Таким образом, выражение эквивалентно (++x
, x*3
).3*x++
, где таблица утверждает, что инкремент применяется только к x а не к 3*x
. Функционально это выражение эквивалентно (tmp=3*x
, ++x
, tmp
), если выразить временную переменную как tmp.Связывание операторов в стандартах C и C++ определено через грамматику языка, а не через таблицу. Это может создать конфликт. Например, в языке Си синтаксис условного оператора таков:
logical-OR-expression ? expression : conditional-expression
А в языке Си++:
logical-OR-expression ? expression : assignment-expression
Из-за этого выражение:
e = a < d ? a++ : a = d
будет воспринято по-разному в этих двух языках. В Си выражение синтаксически некорректно, но многие компиляторы воспринимают его как:
e = ((a < d ? a++ : a) = d)
Этот вариант ошибочен, так как результат условного оператора не является lvalue.
В C++, выражение будет разобрано как корректное:
e = (a < d ? a++ : (a = d))
Приоритеты побитовых логических операторов несколько неинтуитивны.[1] Концептуально &
и |
являются такими же арифметическими операторами как +
и *
.
Выражение a & b == 7 синтаксически воспринимается как a & (b == 7), но выражение a + b == 7 эквивалентно (a + b) == 7. Из-за этого часто требуется пользоваться скобками для явного задания порядка вычислений.
В C++ определены[2] ключевые слова, являющиеся синонимами к некоторым операторам: and (&&), bitand (&), and_eq (&=), or (||), bitor (|), or_eq (|=), xor (^), xor_eq (^=), not (!), not_eq (!=), compl (~)
. Они могут использоваться точно так же как и оператор. Фактически они являются другим вариантом символьной записи тех же операторов. Например, bitand
может использоваться не только для замены побитового оператора И, но и как оператор взятия адреса, или даже при задании ссылочных типов (допустима запись int bitand ref = n;
что эквивалентно int & ref = n;
).
В стандарте ANSI C (ISO C) эти ключевые слова определены как макросы препроцессора в заголовочном файле <iso646.h>
. Для совместимости, в C++ существует фиктивный заголовочный файл <ciso646>
.
Язык программирования Си | |
---|---|
ANSI C (C89 и C90) • C99 • C11 | |
Компиляторы | Borland Turbo C • Clang • GCC • LCC • Pelles C • PCC • TCC • Visual C++ (C++/CLI • C++/CX) • Watcom C/C++ compiler |
Библиотеки | Стандартная библиотека языка Си • glibc • dietlibc • uClibc • Newlib • Eglibc • Bionic |
Особенности | C-строка • Синтаксис • Препроцессор • Заголовочные файлы • Типы и объявления переменных • Функции |
Избранные потомки | C++ • C# • D • Objective-C • Java • Alef • Limbo • Go • Vala |
C и другие языки | C и C++ (Совместимость • Операторы) • Сравнение Pascal и C • Компилятор C в байт-код Java |
Категория:Язык программирования Си |
Операторы в C и C++.