Оператори в C та C++
Тут наведено перелік операторів що використовуються у мовах програмування C та C++. Усі наведені оператори присутні у C++. Якщо оператор також використовується у мові С, це буде відмічено у стовпчику "Присутній у С". Стовпчик "Можливість перевантаження" має сенс лише для мови C++, оскільки C не підтримує перевантаження операторів.
За відсутності перевантаження, для операторів &&, ||, та , (оператор кома), існує точка перебігу після обчислення першого операнду.
C++ також містить наступні оператори приведення типів const_cast, static_cast, dynamic_cast, та reinterpret_cast, які в таблиці не представлені задля скорочення. Форматування цих операторів означає що рівень їх пріоритету не є важливим.
Більшість операторів C та C++ також використовуються у мовах C#, Java, Perl, та PHP з тими самими пріоритетом, асоціативністю та семантикою.
Таблиця
В даній таблиці, символи a, b, та c представляють будь-які дійсні значення (літерали, значення змінних, або повернені значення), імена об'єктів або інші значення що мають адресу.
"Можливість перевантаження" означає, що даний оператор може бути перевантажений засобами C++, але не у мові С, оскільки вона не підтримує перевантаження операторів.
"Присутній у C" означає що даний оператор існує і має семантичний зміст у мові C.
Арифметичні оператори
| Ім'я оператора | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Присвоєння | a = b | Так | Так | T& T::operator =(const T& b); |
Н/Д | |
| Додавання | a + b | Так | Так | T T::operator +(const T& b) const; |
T operator +(const T& a, const T& b); | |
| Віднімання | a - b | Так | Так | T T::operator -(const T& b) const; |
T operator -(const T& a, const T& b); | |
| Унарний плюс (integer promotion) | +a | Так | Так | T T::operator +() const; |
T operator +(const T& a); | |
| Унарний мінус (additive inverse) | -a | Так | Так | T T::operator -() const; |
T operator -(const T& a); | |
| Множення | a * b | Так | Так | T T::operator *(const T& b) const; |
T operator *(const T& a, const T& b); | |
| Ділення | a / b | Так | Так | T T::operator /(const T& b) const; |
T operator /(const T& a, const T& b); | |
| Остача від ділення[Прим 1] | a % b | Так | Так | T T::operator %(const T& b) const; |
T operator %(const T& a, const T& b); | |
| Інкремент | у вигляді префіксу | ++a |
Так | Так | T& T::operator ++(); |
T& operator ++(T& a); |
| у вигляді суфіксу | a++ |
Так | Так | T T::operator ++(int); |
T operator ++(T& a, int); | |
Примітка: C++ використовує фіктивний параметр int для вирізнення префіксного та суфіксного операторів інкременту. | ||||||
| Декремент | у вигляді префіксу | --a | Так | Так | T& T::operator --(); |
T& operator --(T& a); |
| у вигляді суфіксу | a-- | Так | Так | T T::operator --(int); |
T operator --(T& a, int); | |
Примітка: C++ використовує фіктивний параметр int для вирізнення префіксного та суфіксного операторів декременту. | ||||||
Оператори порівняння/відношення
| Оператор name | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Дорівнює | a == b |
Так | Так | bool T::operator ==(const T& b) const; |
bool operator ==(const T& a, const T& b); | |
| Не дорівнює | a != b | Так | Так | bool T::operator !=(const T& b) const; |
bool operator !=(const T& a, const T& b); | |
| Більше | a > b | Так | Так | bool T::operator >(const T& b) const; |
bool operator >(const T& a, const T& b); | |
| Менше | a < b | Так | Так | bool T::operator <(const T& b) const; |
bool operator <(const T& a, const T& b); | |
| Більше або дорівнює | a >= b | Так | Так | bool T::operator >=(const T& b) const; |
bool operator >=(const T& a, const T& b); | |
| Менше або дорівнює | a <= b | Так | Так | bool T::operator <=(const T& b) const; |
bool operator <=(const T& a, const T& b); | |
Логічні оператори
| Ім'я оператора | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Логічне заперечення (НЕ) | !a |
Так | Так | bool T::operator !() const; |
bool operator !(const T& a); | |
| Логічне І | a && b | Так | Так | bool T::operator &&(const T& b) const; |
bool operator &&(const T& a, const T& b); | |
| Логічне АБО | a || b | Так | Так | bool T::operator ||(const T& b) const; |
bool operator ||(const T& a, const T& b); | |
Побітові оператори
| Ім'я оператора | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Побітове НЕ | ~a |
Так | Так | T T::operator ~() const; |
T operator ~(const T& a); | |
| Побітове І | a & b | Так | Так | T T::operator &(const T& b) const; |
T operator &(const T& a, const T& b); | |
| Побітове АБО | a | b | Так | Так | T T::operator |(const T& b) const; |
T operator |(const T& a, const T& b); | |
| Побітове виключаюче АБО | a ^ b | Так | Так | T T::operator ^(const T& b) const; |
T operator ^(const T& a, const T& b); | |
| Побітовий зсув вліво[Прим 2] | a << b | Так | Так | T T::operator <<(const T& b) const; |
T operator <<(const T& a, const T& b); | |
| Побітовий зсув вправо[Прим 2][Прим 3] | a >> b | Так | Так | T T::operator >>(const T& b) const; |
T operator >>(const T& a, const T& b); | |
Складені оператори присвоєння
| Ім'я оператора | Синтаксис | Сенс | Можливість перевантаження | Присутній у C |
Приклади прототипів (T будь-якого типу) | ||
|---|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | ||||||
| Додавання з присвоєнням | a += b |
a = a + b |
Так | Так | T& T::operator +=(const T& b); |
T& operator +=(T& a, const T& b); | |
| Віднімання з присвоєнням | a -= b |
a = a - b |
Так | Так | T& T::operator -=(const T& b); |
T& operator -=(T& a, const T& b); | |
| Множення з присвоєнням | a *= b |
a = a * b |
Так | Так | T& T::operator *=(const T& b); |
T& operator *=(T& a, const T& b); | |
| Ділення з присвоєнням | a /= b |
a = a / b |
Так | Так | T& T::operator /=(const T& b); |
T& operator /=(T& a, const T& b); | |
| Отримання остачі з присвоєнням | a %= b |
a = a % b |
Так | Так | T& T::operator %=(const T& b); |
T& operator %=(T& a, const T& b); | |
| Побітове І з присвоєнням | a &= b |
a = a & b |
Так | Так | T& T::operator &=(const T& b); |
T& operator &=(T& a, const T& b); | |
| Побітове АБО з присвоєнням | a |= b |
a = a | b |
Так | Так | T& T::operator |=(const T& b); |
T& operator |=(T& a, const T& b); | |
| Побітове виключаюче АБО з присвоєнням | a ^= b |
a = a ^ b |
Так | Так | T& T::operator ^=(const T& b); |
T& operator ^=(T& a, const T& b); | |
| Побітовий зсув вліво з присвоєнням | a <<= b |
a = a << b |
Так | Так | T& T::operator <<=(const T& b); |
T& operator <<=(T& a, const T& b); | |
| Побітовий зсув вправо з присвоєнням[Прим 3] | a >>= b |
a = a >> b |
Так | Так | T& T::operator >>=(const T& b); |
T& operator >>=(T& a, const T& b); | |
Операції із вказівниками та членами
| Ім'я оператора | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T, T2 та R будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Індексація масиву | a[b] |
Так | Так | R& T::operator [](const T2& b); |
Н/Д | |
| Розіменування покажчика ("на об'єкт вказує a") | *a | Так | Так | R& T::operator *(); |
R& operator *(T& a); | |
| Вказівник ("отримання адреси змінної a") | &a | Так | Так | T* T::operator &(); |
T* operator &(T& a); | |
| Розкриття посилання на структуру ("на член b об'єкту вказує a") | a->b | Так | Так | R* T::operator ->(); |
Н/Д | |
| Посилання на структуру ("член b об'єкта a") | a.b | Ні | Так | Н/Д | ||
| На член вказує b що належить об'єкту на який вказує a[Прим 4] | a->*b | Так | Ні | R T::operator->*(R);[Прим 5] |
R operator->*(T, R);[Прим 5] | |
| На член вказує b що належить об'єкту a | a.*b | Ні | Ні | Н/Д | ||
Інші оператори
| Ім'я оператора | Синтаксис | Можливість перевантаження | Присутній у C |
Приклади прототипів (T, R, Arg1 та Arg2 будь-якого типу) | ||
|---|---|---|---|---|---|---|
| Як член класу T | Поза описом класу | |||||
| Виклик Функції Дивись Функтор. |
a(a1, a2) |
Так | Так | R T::operator ()(Arg1 a1, Arg2 a2, …); |
Н/Д | |
| Кома | a, b | Так | Так | R& T::operator ,(R& b) const; |
R& operator ,(const T& a, R& b); | |
| Тернарний умовний оператор | a ? b : c | Ні | Так | Н/Д | ||
| Розширення області дії | a::b | Ні | Ні | Н/Д | ||
| Отримання розміру | sizeof(a)[Прим 6]sizeof(type) | Ні | Так | Н/Д | ||
| Отримання вирівнювання | alignof(type) or _Alignof(type)[Прим 7] | Ні | Так | Н/Д | ||
| Ідентифікація типу | typeid(a)typeid(type) | Ні | Ні | Н/Д | ||
| Приведення типу | (type) a | Так | Так | T::operator R() const; |
Н/Д | |
| Примітка: для перетворень, які є визначені користувачем, тип що повертається повинен обов'язково збігатися з ім'ям оператора. | ||||||
| Виділення пам'яті | 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); | |
Примітки:
- Оператор отримання остачі працює лише з цілочисельними операндами, для чисел з рухомою комою має бути застосована відповідна бібліотечна функція (наприклад
fmod). - В контексті потоків введення/виведення бібліотеки iostream, автори часто називають оператори
<<та>>як “вставити в” або "внесення в потік" та “взяти з” або "винесення з потоку", відповідно. - Відповідно до стандарту C99, зсув вправо від'ємного числа залежить від його конкретної реалізації. У більшості реалізацій застосовується арифметичний зсув, але можливим є і логічний зсув.
- Приклад можна знайти у "Implementing operator->* for Smart Pointers" by Scott Meyers.
- У випадку, коли оператор ->* працює за замовчуванням, параметр R буде методом-покажчиком на метод класу Т і повернене значення нагадуватиме деякий функтор, що готовий до виклику з параметрами методу (і тільки ними).
- При визначенні розміру змінної, круглі дужки не обов'язкові, вони потрібні лише при визначенні розміру типу даних. Однак, зазвичай, вони все одно вживаються.
- У мові C++ визначено оператор
alignof, тоді як у C_Alignof. Обидва оператори мають однакову семантику.
Пріоритет операторів
У наступній таблиці вказано пріоритет та асоціативність усіх операторів мов C та C++ (якщо будь-який з вказаних операторів існує Java, Perl, PHP або інший сучасних мовах, його пріоритет, скоріш за все, буде таким самим). Оператори вказані в порядку зниження пріоритету зверху вниз. Зниження пріоритету відноситься до пріоритету обчислень. В процесі обчислення виразу, оператор вказаний у певному рядку таблиці, буде обчислений раніше за оператор, вказаний у будь-якому нижчому за нього рядку. Оператори, вказані в одній комірці, обчислюються з однаковим пріоритетом в порядку їх запису у виразі. При перевантаженні, пріоритет оператора не змінюється.
Синтакс виразів у мовах C та C++ визначається контекстно-вільною граматикою. Наведена вище таблиця отримана на основі граматики.
Слід зазначити, що в деяких випадках таблиця пріоритетів на працює. Наприклад, тернарний умовний оператор дозволяє використовувати як свій середній операнд будь-який довільний вираз, попри те, що за таблицею його пріоритет вищий ніж у операторів присвоєння та коми. Таким чином вираз a ? b , c : d інтерпретується саме як a ? (b, c) : d, а не (a ? b), (c : d) (що не мало б сенсу). Крім того, слід зазначити, що безпосередній результат виразу який перетворює типи, не може бути операндом sizeof. Тому, sizeof (int) * x інтерпретується як (sizeof(int)) * x, а не sizeof ((int) *x).
| Пріоритет | Оператор | Опис | Асоціативність |
|---|---|---|---|
| 1
найвищий |
:: |
Розширення області дії (тільки для C++) | Зліва направо |
| 2 | ++ |
Інкремент у вигляді суфікса | |
-- |
Декремент у вигляді суфікса | ||
() |
Виклик функції | ||
[] |
Індексація масиву | ||
. |
Вибір елементу через посилання | ||
-> |
Вибір елементу через покажчик | ||
typeid() |
Визначення типу в процесі виконання програми (тільки у С++) | ||
const_cast |
Перетворення типів (тільки у C++) | ||
dynamic_cast |
Перетворення типів (тільки у C++) | ||
reinterpret_cast |
Перетворення типів (тільки у C++) | ||
static_cast |
Перетворення типів (тільки у C++) | ||
| 3 | ++ |
Інкремент у вигляді префіксу | Справа наліво |
-- |
Декремент у вигляді префіксу | ||
+ |
Унарний плюс | ||
- |
Унарний мінус | ||
! |
Логічне НЕ | ||
~ |
Побітове НЕ | ||
(type) |
Перетворення типів | ||
* |
Розіменування | ||
& |
Отримання адреси | ||
sizeof |
Отримання розміру | ||
new, new[] |
Динамічне виділення пам'яті (тільки у C++) | ||
delete, delete[] |
Динамічне вивільнення пам'яті (тільки у C++) | ||
| 4 | .* |
Вказівник на член (тільки у C++) | Зліва направо |
->* |
Вказівник на член (тільки у C++) | ||
| 5 | * |
Множення | |
/ |
Ділення | ||
% |
Отримання остачі від ділення | ||
| 6 | + |
Додавання | |
- |
Віднімання | ||
| 7 | << |
Побітовий зсув вліво | |
>> |
Побітовий зсув вправо | ||
| 8 | < |
Менше | |
<= |
Менше або дорівнює | ||
> |
Більше | ||
>= |
Більше або дорівнює | ||
| 9 | == |
Дорівнює | |
!= |
Не дорівнює | ||
| 10 | & |
Побітове І | |
| 11 | ^ |
Побітове виключне АБО | |
| 12 | | |
Побітове АБО | |
| 13 | && |
Логічне І | |
| 14 | || |
Логічне АБО | |
| 15 | ?: |
Тернарний умовний оператор | Справа наліво |
| 16 | = |
Пряме присвоєння | |
+= |
Присвоєння суми | ||
-= |
Присвоєння різниці | ||
*= |
Присвоєння добутку | ||
/= |
Присвоєння частки | ||
%= |
Присвоєння остачі | ||
<<= |
Присвоєння побітового зсуву вліво | ||
>>= |
Присвоєння побітового зсуву вправо | ||
&= |
Присвоєння побітового І | ||
^= |
Присвоєння побітового виключного АБО | ||
|= |
Присвоєння побітового АБО | ||
| 17 | throw |
Створення тестової помилки (винятку) (тільки у C++) | |
| 18 | , |
Кома | Зліва направо |
Примітки
Таблиця пріоритету визначає порядок застосування (поєднання) операторів у складних виразах, коли цей порядок не заданий явно за допомогою круглих дужок.
- Наприклад, вираз
++x*3без застосування правил пріоритету має певну двозначність. Таблиця пріоритету каже нам, що: x тісніше 'пов'язаний' з ++ ніж з *, тому щоб не робив оператор ++ (зараз чи пізніше), він робитиме це ТІЛЬКИ з x (а не зx*3); це еквівалентно виразу (++x,x*3). - Аналогічним чином, для виразу
3*x++, де постфіксний оператор інкременту ++ хоча і призначений діяти ПІСЛЯ обчислення усього виразу, але таблиця пріоритетів ясно вказує, що не зважаючи на це, інкрементується ТІЛЬКИ x (а не3*x); це еквівалентно виразу подібному до (tmp=3*x,++x,tmp), де tmp є тимчасовим значенням.

- Зобразимо проблему пріоритету операторів та їх поєднання у вигляді діаграми. Завданням компілятора є перетворення цієї діаграми у вираз, в якому декілька унарних операторів ( назвемо їх 3+( . ), 2*( . ), ( . )++ та ( . )[ i ] ) конфліктують за поєднання з y. Проблема вирішується за допомогою таблиці пріоритету, яка дозволяє сформувати остаточний набір виразів: ( . )[ i ] діє тільки на y, ( . )++ діє тільки на y[i], 2*( . ) діє тільки на y[i]++ та 3+( . ) діє 'тільки' на 2*((y[i])++). Важливо зазначити, що таблиця дозволяє визначити ЯКИЙ з виразів буде використаний кожним з операторів, але відповіді на питання "КОЛИ кожний оператор вступить в дію", таблиця не дає. В даному прикладі оператор ( . )++ діє тільки на y[i] за правилами таблиці, але самі по собі рівні поєднання не визначають час виконання суфікса ++ (оператор ( . )++ буде використаний лише після обчислення у виразі елементу y[i]).
Ряд багатосимвольних операторів отримують власні "імена", побудовані з назв операторів для кожного свого символу. Наприклад, оператори += та -= часто називаються плюс дорівнює та мінус дорівнює, а не "присвоєння суми" та "присвоєння різниці", що було б громіздкіше.
Поєднання операторів у C та C++ визначається скоріше граматикою мови (викладеною у відповідних стандартах), а не таблицею пріоритету. Це створює деякі приховані конфлікти. Наприклад, у мові С, синтаксис для виразу з умовним переходом є таким:
logical-OR-expression ? expression : conditional-expression
в той час коли у C++ він такий:
logical-OR-expression ? expression : assignment-expression
Таким чином вираз:
e = a < d ? a++ : a = d
у двох мовах граматично розбивається по різному. У мові C цей вираз є синтаксичною помилкою, але багато компіляторів розбивають вираз так:
e = ((a < d ? a++ : a) = d)
що є семантичною помилкою, оскільки результат виразу з умовним переходом (яким може бути a++) не є адресованим значенням (lvalue). У C++ це розбивається як:
e = (a < d ? a++ : (a = d))
що є правильним виразом.
Пріоритет побітових операторів було розкритиковано . Концептуально, оператори & and | є арифметичними операторами, подібними до + and *.
Вираз a & b == 7 синтаксично розбивається як a & (b == 7), коли вираз a + b == 7 розбивається як (a + b) == 7. Це потребує частішого застосування круглих дужок.
Синоніми операторів C++
C++ визначає[1]
набір ключових слів, які можуть діяти як псевдоніми деяких операторів: 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 резервує ці ключові слова як макроси препроцесора у заголовному файлі iso646.h. З метою збереження сумісності із C, у мові C++ передбачено заголовний файл ciso646, додавання якого не дає жодного ефекту.
Посилання
- ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee (1 September 1998). ISO/IEC 14882:1998(E) Programming Language C++. International standardization working group for the programming language C++. с. 40–41.