Operatori in C e C++

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

Questa è una lista degli operatori nei linguaggi di programmazione C e C++. Tutti gli operatori seguenti sono implementabili in quest'ultimo linguaggio, mentre invece altri non lo sono in C, come nel caso degli operatori di conversione di tipo (casting), ossia const_cast, static_cast, dynamic_cast, e reinterpret_cast[1][2][3].

Molti degli operatori disponibili in C e C++ sono implementabili pure in altri linguaggi della cosiddetta “famiglia C”, quali C#, D, ma anche Java, Perl e PHP, mantenendo le medesime caratteristiche mostrate (arietà, posizione e associatività)[4][5].

Sintassi degli operatori

[modifica | modifica wikitesto]

Nelle tabelle seguenti, a, b e c rappresentano valori validi qualsiasi (literals, valori da variabili, oppure return value) o, in alcuni casi specifici, nomi di oggetti o lvalues. R, S e T indicano qualsiasi tipo, mentre K indica un tipo di classe o un tipo enum[6][7].

Operatori aritmetici

[modifica | modifica wikitesto]

Tutti gli operatori aritmetici esistono sia in C e C++ e possono essere, come già indicato, sovraccaricati solo in C++[8].

Nome operatore Sintassi Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Addizione a+b R K::operator +(S b); R operator +(K a, S b);
Sottrazione a-b R K::operator -(S b); R operator -(K a, S b);
Unario più +a R K::operator +(); R operator +(K a);
Unario meno -a R K::operator -(); R operator -(K a);
Moltiplicazione a * b R K::operator *(S b); R operator *(K a, S b);
Divisione a / b R K::operator /(S b); R operator /(K a, S b);
Modulo a % b R K::operator %(S b); R operator %(K a, S b);
Incremento Prefisso ++a R& K::operator ++(); R& operator ++(K& a);
Postfisso a++ R K::operator ++(int); R operator ++(K& a, int);
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso.
Decremento Prefisso --a R& K::operator --(); R& operator --(K& a);
Postfisso a-- R K::operator --(int); R operator --(K& a, int);
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso.

Operatori relazionali

[modifica | modifica wikitesto]

Tutti gli operatori di confronto possono essere sovraccaricati solo in C++. Dal C++20, l'operatore di disuguaglianza viene generato automaticamente se viene definito operator== e tutti e quattro gli operatori relazionali vengono generati automaticamente se viene definito operator<=>.

Nome operatore Sintassi Incluso in C Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Uguale a = b bool K::operator ==(S const& b) const; bool operator ==(K const& a, S const& b);
Diverso a != b bool K::operator !=(S const& b) const; bool operator !=(K const& a, S const& b);
Maggiore di a > b bool K::operator >(S const& b) const; bool operator >(K const& a, S const& b);
Minore di a < b bool K::operator <(S const& b) const; bool operator <(K const& a, S const& b);
Maggiore o uguale a a >= b bool K::operator >=(S const& b) const; bool operator >=(K const& a, S const& b);
Minore o uguale a a <= b bool K::operator <=(S const& b) const; bool operator <=(K const& a, S const& b);

Operatori logici (o booleani)

[modifica | modifica wikitesto]

Tutti gli operatori logici (o booleani[9]) sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricati solo in quest'ultimo linguaggio di programmazione.

Nonostante la possibilità di sovraccarico in C++, tale operazione è sconsigliata con AND logico e OR logico sia sconsigliato perché, come operatori sovraccaricati, si comporterebbero come normali chiamate di funzione, il che significa che entrambi i loro operandi verrebbero valutati, perdendo di conseguenza la loro importante valutazione di McCarthy. [10]

Nome dell'operatore Sintassi Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
NOT logico ! a bool K::operator !(); bool operator !(K a);
AND logico a && b bool K::operator &&(S b); bool operator &&(K a, S b);
OR logico a || b bool K::operator ||(S b); bool operator ||(K a, S b);

Operatori bit a bit

[modifica | modifica wikitesto]

Tutti gli operatori bit a bit (o di manipolazione dei bit) esistono sia C che C++ e possono essere sovraccaricati soltanto nel linguaggio inventato da Bjarne Stroustrup[11].

Nome dell'operatore Sintassi Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Complemento a uno (inversione di tutti i bit) ~a R K::operator ~(); R operator ~(K a);
AND bit a bit a & b R K::operator &(S b); R operator &(K a, S b);
OR inclusivo bit a bit a | b R K::operator |(S b); R operator |(K a, S b);
OR esclusivo bit a bit (OR exclusive, XOR) a ^ b R K::operator ^(S b); R operator ^(K a, S b);
Spostamento di tutti i bit verso sinistra a << b R K::operator <<(S b); R operator <<(K a, S b);
Spostamento di tutti i bit verso destra a >> b R K::operator >>(S b); R operator >>(K a, S b);

Operatori ed espressioni di assegnamento

[modifica | modifica wikitesto]

Tutti gli operatori e le espressioni di assegnamento sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricate solo in quest'ultimo linguaggio di programmazione.

Per gli operatori indicati, la semantica dell'espressione di assegnazione combinata incorporata a ⊚= b è equivalente a a = a ⊚ b, eccetto per il fatto che a viene valutata una sola volta.

Tipologia assegnamento Sintassi Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Espressione semplice di assegnamento a = b R& K::operator =(S b); N.D.
Assegnamenti composti a += b R& K::operator +=(S b); R& operator +=(K& a, S b);
a -= b R& K::operator -=(S b); R& operator -=(K& a, S b);
a *= b R& K::operator *=(S b); R& operator *=(K& a, S b);
a /= b R& K::operator /=(S b); R& operator /=(K& a, S b);
a %= b R& K::operator %=(S b); R& operator %=(K& a, S b);
a &= b R& K::operator &=(S b); R& operator &=(K& a, S b);
a |= b R& K::operator |=(S b); R& operator |=(K& a, S b);
a ^= b R& K::operator ^=(S b); R& operator ^=(K& a, S b);
a <<= b R& K::operator <<=(S b); R& operator <<=(K& a, S b);
a >>= b R& K::operator >>=(S b); R& operator >>=(K& a, S b);

Operatori membro e puntatore

[modifica | modifica wikitesto]
Nome operatore[12] Sintassi Ammette overload (sovraccarico) in C++ Implementabile in C Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Indicizzazione a[b] R& K::operator [](S b);

R& K::operator [](S b, ...); // since C++23

N.D.
Deferenziazione *a R& K::operator *(); R& operator *(K a);
Indirizzo &a R* K::operator &(); R* operator &(K a);
Deferenziazione e selezione a->b R* K::operator ->(); N.D.
Selezione (object.member) a.b No N.D. N.D.
Deferenziazione e selezione con puntatore a membro a->*b No R& K::operator ->*(S b); R& operator ->*(K a, S b);
Selezione con puntatore a membro a.*b No No N.D. N.D.

Altri operatori

[modifica | modifica wikitesto]
Nome operatore Sintassi Ammette overload (sovraccarico) in C++ Implementabile in C Esempi di implementazione in C++
Come membro della classe K Fuori dalla classe
Chiamata di funzione a(a1, a2) R K::operator ()(S a, T b, ...);
Virgola a, b R K::operator ,(S b); R operator ,(K a, S b);
Espressione condizionale a ? b : c No N.D. N.D.
Risolutore di visibilità a::b No No N.D. N.D.
Dimensione di oggetto o di tipo (sizeof) sizeof a

sizeof (R)

No N.D. N.D.
Conversione di tipo static_cast static_cast<R>(a) No K::operator R();

explicit K::operator R(); since C++11[13]

N.D.
Conversione const const_cast const_cast<R>(a) No No N.D. N.D.
Allocazione new R No void* K::operator new(size_t x); void* operator new(size_t x);
Allocazione di array new R[n] No void* K::operator new[](size_t a); void* operator new[](size_t a);
Deallocazione delete a No void K::operator delete(void* a); void operator delete(void* a);
Deallocazione di array delete[] a No void K::operator delete[](void* a); void operator delete[](void* a);

Overloading degli operatori

[modifica | modifica wikitesto]

C++ possiede molti operatori già predefiniti in grado di operare su vari tipi di dato, perciò si può parlare di “operatori già sovraccaricati”. Per esempio l’operatore + può essere implementato per sommare sia due variabili di tipo int (interi), sia due variabili di tipo float o double (variabili a virgola mobile).

Oltre a quando detto in precedenza, il programmatore può anche “estendere” l’operatività di ciascuno di essi; per esempio operandi complessi definiti con le classi, si potrebbero trattare anche come se fossero dati semplici[1].

In C++ è possibile sovraccaricare tutti gli operatori predefiniti, con l’eccezione su accesso al membro (.), indirezione di accesso al membro (.*) e risoluzione di ambito (::).

Nel caso degli operatori, il numero degli operandi e la priorità dell’operatore sovraccaricato sono le stesse dell’operatore predefinito, mentre il nuovo operatore può essere definito come una funzione membro oppure come una funzione friend (funzione amica)[14].

Proprietà degli operatori

[modifica | modifica wikitesto]

Ogni operatore possiede delle proprietà, in modo tale da permettere l'interpretazione univoca del significato di ogni singola espressione. Gli esempi che seguono sono tutti applicabili al linguaggio C++ e, a seconda dei casi, anche al C[15].

Un operatore può precedere gli operandi (o argomenti), seguirli oppure né precederli né seguirli[12]. In questi casi parleremo rispettivamente di operatori prefissi, postfissi oppure infissi.

Numero di operandi

[modifica | modifica wikitesto]

Ogni operatore può avere un numero diverso di operandi (arietà).

a[b]

Nel caso riportato, l'operatore di indicizzazione possiede due operandi, ossia le parentesi quadre. L'arietà può essere anche di tre operandi, come nel caso degli operatori condizionali, riportato di seguito:

e1 ? e2 : e3

Precedenza degli operatori

[modifica | modifica wikitesto]

Ogni operatore possiede una propria precedenza[16][17], rappresentata attraverso un numero naturale tra 1 e 18 in ordine decrescente, dove più è piccolo il numero, maggiore sarà la priorità; per esempio un operatore con priorità 1 avrà maggiore priorità su un operatore con priorità 13.

Nell'esempio che segue è possibile osservare una semplice applicazione pratica delle precedenze:

#include <iostream>
using namespace std;

int main () {
int a = 5;
int b = 6;
int c = 8;

int op = a+b*c;
cout << op << endl; // Stampa a schermo

return 0;
}

L'operatore moltiplicazione (*) possiede priorità 5, mentre l'operatore addizione (+) possiede priorità 6; pertanto il risultato a schermo sarà 53 e non 19.

Associatività degli operatori

[modifica | modifica wikitesto]

L'associatività indica l'ordine in cui vengono eseguiti gli operatori aventi la stessa priorità tra loro[12]. Viene riportato un semplice esempio con gli operatori di divisione (priorità 5).

#include <iostream>
using namespace std;

int main (){
        float a = 5.0;
        float b = 6.0;
        float c = 8.0;

        float div = a/b/c;
        cout << div << endl; // Stampa a schermo

        return 0;
}

Nell'esempio precedente la stampa a schermo sarà 0.104167 perché l'operatore di divisione segue associatività a sinistra e, quindi, il calcolatore avrà eseguito la seguente equazione: .

Tabella delle precedenze e delle associatività

[modifica | modifica wikitesto]

Nella tabella seguente sono elencati gli operatori di C e C++ in ordine di priorità con le loro rispettive associatività. Essi sono sempre implementabili su C++, mentre, quelli appositamente indicati nella penultima colonna da sinistra, non lo sono in C[12][17].

Priorità Operatore Nome Implementabile in C Associatività
1

(priorità massima)

class-name :: member

namespace-name :: member

Risolutore di visibilità No sinistra
:: member Risolutore globale di visibilità No destra
2 object . member Selezione sinistra
pointer -> member Deferenziazione e selezione sinistra
array[ rvalue ] Indicizzazione sinistra
function ( actual-argument-list ) Chiamata di funzione sinistra
lvalue ++ Postincremento destra
lvalue -- Postdecremento destra
static_cast type<rvalue> Conversione di tipo No sinistra
const_cast type<rvalue> Conversione const No sinistra
3 sizeof object

sizeof (type)

Dimensione

di oggetto di tipo

destra
++ lvalue Preincremento destra
-- lvalue Predecremento destra
~ rvalue Complemento bit a bit destra
! rvalue Negazione destra
- rvalue Meno unitario destra
+ rvalue Più unitario destra
& rvalue Indirizzo destra
* rvalue Deferenziazione destra
new type Allocazione No destra
new type[] Allocazione di array No destra
delete pointer Deallocazione No destra
delete[] pointer Deallocazione di array No destra
4 object .* pointer-to-member Selezione con puntatore a membro No sinistra
pointer ->* pointer-to-member Deferenziazione e selezione con puntatore a membro No sinistra
5 rvalue * rvalue Moltiplicazione sinistra
rvalue / rvalue Divisione sinistra
rvalue % rvalue Modulo sinistra
6 rvalue + rvalue Addizione sinistra
rvalue - rvalue Sottrazione sinistra
7 rvalue << rvalue Traslazione sinistra sinistra
rvalue >> rvalue Traslazione destra sinistra
8 rvalue < rvalue Minore sinistra
rvalue <= rvalue Minore o uguale sinistra
rvalue > rvalue Maggiore sinistra
rvalue >= rvalue Maggiore o uguale sinistra
9 rvalue == rvalue Uguale sinistra
rvalue != rvalue Diverso sinistra
10 rvalue & rvalue AND bit a bit sinistra
11 rvalue ^ rvalue OR esclusivo bit a bit sinistra
12 rvalue | rvalue OR bit a bit sinistra
13 rvalue && rvalue AND logico sinistra
14 rvalue || rvalue OR logico sinistra
15 co_await

co_yield

Coroutine No destra
16 rvalue ? rvalue : rvalue Espressione condizionale sinistra
17 lvalue = rvalue Assegnamento destra
lvalue += rvalue Addizione e assegnamento destra
lvalue -= rvalue Sottrazione e assegnamento destra
lvalue *= rvalue Moltiplicazione e assegnamento destra
lvalue /= rvalue Divisione e assegnamento destra
lvalue %= rvalue Modulo e assegnamento destra
lvalue &= rvalue AND bit a bit e assegnamento destra
lvalue |= rvalue OR bit a bit e assegnamento destra
lvalue ^= rvalue OR esclusivo bit a bit e assegnamento destra
lvalue <<= rvalue Traslazione a sinistra e assegnamento destra
lvalue =>> rvalue Traslazione a destra e assegnamento No destra
18

(priorità minima)

expression , expression Virgola sinistra

Critiche sulle precedenze degli operatori

[modifica | modifica wikitesto]

La precedenza degli operatori logici bitwise è stata al centro di numerose critiche[18][19], poiché concettualmente, & e | sono operatori aritmetici come * e +.

Per esempio, l'espressione a & b == 7 viene sintatticamente analizzata come a & (b == 7), mentre l'espressione a + b == 7 viene analizzata come (a + b) == 7. Ciò richiede l'uso delle parentesi più spesso di quanto sarebbe altrimenti necessario.

Storicamente, non esisteva una distinzione sintattica tra gli operatori bit a bit e quelli logici. Nei linguaggi BCPL, B e nelle prime versioni di C, gli operatori && e || non esistevano proprio; invece, & | aveva un significato diverso a seconda che venisse usato in un “contesto di valore di verità” (cioè quando ci si aspettava il ritorno di un valore booleano, come in if (a==b & c) {...} e si comportava come un operatore logico, ma in c = a & b si comportava invece come un operatore bit a bit).

La sintassi attuale è stata mantenuta soltanto per consentire la retrocompatibilità con le installazioni già esistenti.

Peraltro, in C++ (e nelle versioni successive di C) le operazioni di uguaglianza, con l'eccezione dell'operatore di confronto logico a tre, producono valori di tipo bool che sono concettualmente un singolo bit (1 o 0) e come tali non appartengono propriamente alle operazioni bit a bit.

Sinonimi in C++

[modifica | modifica wikitesto]

C++ definisce una serie di keywords che possono essere implementate come sinonimi rispetto agli operatori[20]; tali definizioni non sono assolutamente implementabili nel linguaggio C.

Keyword Operator
and &&
and_eq &=
bitand &
bitor |
compl ~
not !
not_eq !=
or ||
or_eq |=
xor ^
xor_eq ^=

Questi possono essere utilizzati esattamente come sostituti dei rispettivi simboli di punteggiatura; ciò significa, per esempio, che le espressioni (a > 0 AND NOT flag) e (a > 0 && flag) avranno un significato assolutamente identico.

  1. ^ a b Luis Joyanes Aguilar, Fondamenti di programmazione in C++. Algoritmi, strutture dati e oggetti, II edizione in lingua italiana, Madrid-Milano, McGraw-Hill Education, 2008, ISBN 978-88-386-6477-9.
  2. ^ Stanley Lippman, Josée Lajoie e Barbara Moo, C++ Primer, V edizione, Addison-Wesley Professional, 2013, ISBN 978-0321714114.
  3. ^ Conversione di tipo, su HTML.it. URL consultato il 14 settembre 2024.
  4. ^ Il linguaggio C#, su www.andreaminini.com. URL consultato il 30 agosto 2024.
  5. ^ Operator Overloading - D Programming Language, su dlang.org. URL consultato il 30 agosto 2024.
  6. ^ 1.3 Operatori., su www.math.unipd.it. URL consultato il 30 agosto 2024.
  7. ^ Operatori booleani, su HTML.it. URL consultato il 30 agosto 2024.
  8. ^ (EN) C++ | Definition, History, & Facts | Britannica, su www.britannica.com. URL consultato il 30 agosto 2024.
  9. ^ L'accezione booleana è stata coniata in onore del matematico George Boole.
  10. ^ isocpp.org, https://isocpp.org/wiki/faq/operator-overloading.
  11. ^ Linguaggio c++ - Enciclopedia, su Treccani. URL consultato il 30 agosto 2024.
  12. ^ a b c d Andrea Domenici e Graziano Frosini, Introduzione alla programmazione ed elementi di strutture dati con il linguaggio C++, collana "Informatica", diretta da A. L. Frisiani, VIII edizione, Milano/Pisa, ISBN 978-8846462022.
  13. ^ Per le conversioni definite dall'utente, il tipo di ritorno corrisponde implicitamente e necessariamente al nome dell'operatore, a meno che il tipo non sia dedotto. (es. operator auto(), operator decltype(auto)() etc.).
  14. ^ Marco Cococcioni, Le classi nel linguaggio C++ (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
  15. ^ Marco Cococcioni, Programmazione in stile C, utilizzando il linguaggio C++ (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
  16. ^ The ISO C 1999 standard, section 6.5.6 note 71 (Technical report). ISO. 1999.
  17. ^ a b Marco Cococcioni, Priorità degli operatori (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
  18. ^ Chistory, su www.bell-labs.com. URL consultato il 30 agosto 2024.
  19. ^ Re^10: next unless condition, su www.perlmonks.org. URL consultato il 30 agosto 2024.
  20. ^ ISO/IEC 14882:1998(E) Programming Language C++. open-std.org – The C++ Standards Committee. 1 September 1998. pp. 40–41.

Voci correlate

[modifica | modifica wikitesto]

Collegamenti esterni

[modifica | modifica wikitesto]