Оператори в 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); N/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);
Унарний плюс (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);
N/A
Розкриття покажчика ("на об'єкт вказує 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 ->();
N/A
Посилання на структуру ("член b об'єкта a") a.b Ні Так N/A
На член вказує b що належить об'єкту на який вказує a[Прим 4] a->*b Так Ні R T::operator->*(R);[Прим 5] R operator->*(T, R);[Прим 5]
На член вказує b що належить об'єкту a a.*b Ні Ні N/A

Інші оператори[ред.ред. код]

Ім'я оператора Синтаксис Можливість перевантаження Присутній у
C
Приклади прототипів (T, R, Arg1 та Arg2 будь-якого типу)
Як член класу T Поза описом класу
Виклик Функції
Дивись Функтор.
a(a1, a2) Так Так R T::operator ()(Arg1 a1, Arg2 a2, …); N/A
Кома a, b Так Так R& T::operator ,(R& b) const; R& operator ,(const T& a, R& b);
Тернарний умовний оператор a ? b : c Ні Так N/A
Розширення області дії a::b Ні Ні N/A
Отримання розміру sizeof(a)[Прим 6]
sizeof(type)
Ні Так N/A
Отримання вирівнювання alignof(type)
or _Alignof(type)[Прим 7]
Ні Так N/A
Ідентифікація типу typeid(a)
typeid(type)
Ні Ні N/A
Перетворення типу (type) a Так Так T::operator R() const; N/A
Примітка: для перетворень, які є визначені користувачем, тип що повертається повинен обов'язково збігатися з ім'ям оператора.
Виділення пам'яті 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);

Примітки:

  1. Оператор отримання залишку працює лише з цілочисельними операндами, для чисел з плаваючою комою має бути застосована відповідна бібліотечна функція (наприклад fmod).
  2. а б В контексті потоків введення/виведення бібліотеки iostream, автори часто називають оператори << та >> як “вставити в” або "внесення в потік" та “взяти з” або "винесення з потоку", відповідно.
  3. а б Відповідно до стандарту C99, зсув вправо від'ємного числа залежить від його конкретної реалізації. У більшості реалізацій застосовується арифметичний зсув, але можливим є і логічний зсув.
  4. Приклад можна знайти у "Implementing operator->* for Smart Pointers" by Scott Meyers.
  5. а б У випадку, коли оператор ->* працює за замовчуванням, параметр R буде методом-покажчиком на метод класу Т і повернене значення нагадуватиме деякий функтор, що готовий до виклику з параметрами методу (і тільки ними).
  6. При визначенні розміру змінної, круглі дужки не обов'язкові, вони потрібні лише при визначенні розміру типу даних. Однак, зазвичай, вони все одно вживаються.
  7. У мові 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))

що є правильним виразом.

Пріоритет побітових операторів було розкритиковано [1]. Копцептуально, оператори & 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, додавання якого не дає жодного ефекту.

Посилання[ред.ред. код]

  1. 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. 

Зовнішні посилання[ред.ред. код]