Operatore virgola

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

Nei linguaggi di programmazione C e C++, l'operatore virgola (rappresentato dal token ,) è un operatore binario che valuta i suoi due operandi in ordine e restituisce il risultato del secondo. È presente un punto di sequenza tra queste valutazioni.

L'uso del token virgola come operatore è distinto dal suo utilizzo in chiamate di funzione, definizioni, dichiarazioni di variabili, dichiarazioni enum e costrutti simili, in cui funge da separatore.

Sintassi[modifica | modifica wikitesto]

L'operatore virgola separa le espressioni (che hanno valore) in modo analogo a come il punto e virgola termina le istruzioni. Le sequenze di espressioni sono racchiuse tra parentesi analogamente a come le sequenze di istruzioni sono racchiuse tra parentesi graffe:[1] (a, b, c) è una sequenza di espressioni, separate da virgole, che restituisce l'ultima espressione c mentre {a; b; c;} è una sequenza di istruzioni e non restituisce alcun valore. Una virgola può trovarsi solo tra due espressioni (la quale le separa) a differenza del punto e virgola, che si trova alla fine di un'istruzione (non di blocco), i punti e virgola terminano le istruzioni.

L'operatore virgola ha la precedenza più bassa di qualsiasi operatore C e funge da punto di sequenza. In una combinazione di virgole e punti e virgola, i punti e virgola hanno una precedenza inferiore alle virgole, poiché i punti e virgola separano le affermazioni ma le virgole si trovano all'interno delle affermazioni, il che concorda con il loro uso come punteggiatura ordinaria: a, b; c, d è raggruppato come (a, b); (c, d) perché si tratta di due affermazioni separate.

Esempi[modifica | modifica wikitesto]

In questo esempio, il comportamento diverso tra la seconda e la terza riga è dovuto al fatto che l'operatore virgola ha una precedenza inferiore rispetto all'assegnazione. Anche l'ultimo esempio è diverso poiché l'espressione di ritorno deve essere valutata completamente prima che la funzione possa ritornare.

/**
 * Le virgole si comportano da separatori in questa linea, non come operatori.
 * Valori: a=1, b=2, c=3, i=0
 */
int a=1, b=2, c=3, i=0;

/**
 * Assegna il valore di b in i.
 * Le virgole si comportano da separatori nella prima linea e da operatore nella seconda.
 * Valori: a=1, b=2, c=3, i=2
 */
int a=1, b=2, c=3;       
int i = (a, b);

/**
 * Assegna il valore di a in i.
 * Equivalente a: int i = a; int b;
 * Le virgole si comportano da separatori in entrambe le linee.
 * Le parentesi nella seconda linea servono ad evitare che vengano ridichiarate le variabili nello stesso blocco,
 * ciò causerebbe un errore di compilazione.
 * Alla seconda b dichiarata non viene dato nessun valore iniziale.
 * Valori: a=1, b=2, c=3, i=1
 */
int a=1, b=2, c=3;                
{ int i = a, b; }

/**
 * Incrementa il valore di b di 2, poi assegna il risultato dell'operazione a + b in i.
 * Le virgole si comportano da separatori nella prima linea e come operatore nella seconda.
 * Valori: a=3, b=2, c=3, i=5
 */
int a=1, b=2, c=3;
int i = (a += 2, a + b);

/**
 * Incrementa il valore di a di 2, poi salva il valore di a in a, e scarta il
 * risultato inutilizzato dell'operazione a + b.
 * Equivalente a: (i = (a += 2)), a + b;
 * Le virgole si comportano da separatori nella prima linea e come operatore nella terza linea.
 * Valori: a=3, b=2, c=3, i=3
 */
int a=1, b=2, c=3;
int i;
i = a += 2, a + b;

/**
 * Assegna il valore di a in i.
 * Le virgole si comportano da separatori in entrambe le linee.
 * Le parentesi nella seconda linea servono ad evitare che vengano ridichiarate le variabili nello stesso blocco,
 * ciò causerebbe un errore di compilazione.
 * Alle seconde b e c dichiarate non viene dato nessun valore iniziale.
 * Valori: a=1, b=2, c=3, i=1
 */
int a=1, b=2, c=3;
{ int i = a, b, c; }

/**
 * Le virgole si comportano da separatori nella prima linea e da operatore nella seconda.
 * Assegna il valore di c in i, scartano i valori inutilizzati di a e di b.
 * Valori: a=1, b=2, c=3, i=3
 */
int a=1, b=2, c=3;
int i = (a, b, c);

/**
 * Ritorna 6, non 4; poiché i punti di sequenza dell'operatore virgola dopo la parola
 * chiave return sono considerati una singola espressione valutando l'rvalue della
 * sottoespressione finale c=6.
 * Le virgole si comportano da operatori in questa linea.
 */
return a=4, b=5, c=6;

/**
 * Ritorna 3, non 1, per gli stessi motivi dell'esempio precedente.
 * Le virgole si comportano da operatori in questa linea.
 */
return 1, 2, 3;

/**
 * Ritorna 3, non 1, ancora per lo stesso motivo di cui sopra. Questo esempio funziona così
 * perché il return è una keyword, non una chiamata di funzione. Anche se i compilatori
 * consentono il costrutto return(valore), le parentesi sono solo relative a "valore"
 * e non hanno alcun effetto speciale sulla parola chiave return.
 * Il return semplicemente riceve un espressione e in questo caso l'espressione è "(1), 2, 3".
 * Le virgole si comportano da operatori in questa linea.
 */
return(1), 2, 3;

Usi[modifica | modifica wikitesto]

L'uso dell'operatore virgola è relativamente limitato. Poiché scarta il suo primo operando, è generalmente utile solo quando il primo operando ha effetti collaterali desiderabili che devono essere sequenziati prima del secondo operando. Inoltre, poiché è usato raramente al di fuori di idiomi specifici e facilmente scambiato con altre virgole o punto e virgola, è potenzialmente fonte di confusione e soggetto a errori. Tuttavia, ci sono alcune circostanze in cui è comunemente usato, in particolare nei cicli for e in SFINAE[2]. Per i sistemi embedded che possono avere capacità di debug limitate, l'operatore virgola può essere utilizzato in combinazione con una macro per sovrascrivere senza problemi una chiamata di funzione, per inserire codice subito prima della chiamata di funzione.

Cicli for[modifica | modifica wikitesto]

L'uso più comune consiste nel consentire più istruzioni di assegnazione senza utilizzare un'istruzione di blocco, principalmente nell'inizializzazione e nelle espressioni di incremento di un ciclo for. Questo è l'unico uso idiomatico nella programmazione C elementare. Nell'esempio seguente, l'ordine degli inizializzatori del ciclo è significativo:

void rev(char *s, size_t len)
{
  char *first;
  for (first = s, s += len; s >= first; --s) {
    putchar(*s);
  }
}

Una soluzione alternativa a questo problema in altri linguaggi è l'assegnazione parallela, che consente di eseguire più assegnazioni all'interno di una singola istruzione e utilizza anche una virgola, sebbene con sintassi e semantica diverse. Questa è usata in Go nel suo analogo ciclo for.[3] Al di fuori degli inizializzatori di ciclo for (che hanno un uso speciale del punto e virgola), la virgola potrebbe essere utilizzata al posto del punto e virgola, in particolare quando le istruzioni in questione funzionano in modo simile a un incremento del ciclo (ad esempio alla fine di un ciclo while):

++p, ++q;
++p; ++q;

Macro[modifica | modifica wikitesto]

La virgola può essere utilizzata nelle macro del preprocessore per eseguire più operazioni nello spazio di una singola espressione sintattica.

Un uso comune consiste nel fornire messaggi di errore personalizzati nelle asserzioni non riuscite. Questo viene fatto passando un elenco di espressioni tra parentesi alla macro assert, dove la prima espressione è una stringa di errore e la seconda espressione è la condizione che viene asserita. La macro assert restituisce il suo argomento testualmente sull'asserzione fallita. Quanto segue è un esempio:

#include <stdio.h>
#include <assert.h>

int main ( void )
{
  int i;
  for (i=0; i<=9; i++)
  {
    assert( ( "i è troppo grande!", i <= 4 ) );
    printf("i = %i\n", i);
  }
  return 0;
}

Output:

i = 0
i = 1
i = 2
i = 3
i = 4
assert: assert.c:6: test_assert: Assertion `( "i è troppo grande!", i <= 4 )' failed.
Aborted

Tuttavia la macro assert è solitamente disabilitata nel codice di produzione, quindi va utilizzata solo per scopi di debug.

Condizione[modifica | modifica wikitesto]

La virgola può essere utilizzata all'interno di una condizione (di un if, while, do-while o for) per consentire calcoli ausiliari, in particolare chiamando una funzione e utilizzando il risultato, rispettando l'ambito del blocco:

if (y = f(x), y > x) {
  ... // istruzioni che utilizzano x e y
}

Un linguaggio simile esiste in Go, dove la sintassi dell'istruzione if consente esplicitamente un'istruzione facoltativa.[4]

Ritorno complesso[modifica | modifica wikitesto]

La virgola può essere utilizzata nelle istruzioni return, per assegnare una variabile globale o un parametro di uscita (passato per riferimento). Questo idioma suggerisce che gli assegnamenti fanno parte del ritorno rispetto ad essere assegnamenti ausiliari in un blocco che termina con il ritorno effettivo. Ad esempio, nell'impostazione di un numero di errore globale:

if (condizione_di_errore)
  return (errno = EINVAL, -1);

Questo può essere scritto in modo più verboso come:

if (condizione_di_errore) {
  errno = EINVAL;
  return -1;
}

Per evitare un blocco[modifica | modifica wikitesto]

Per brevità, la virgola può essere utilizzata per evitare un blocco e le parentesi graffe associate:

if (x == 1) y = 2, z = 3;
if (x == 1)
  y = 2, z = 3;
if (x == 1) {y = 2; z = 3;}
if (x == 1) {
  y = 2; z = 3;
}

Altri linguaggi[modifica | modifica wikitesto]

Nei linguaggi di programmazione OCaml e Ruby, a questo scopo viene utilizzato il punto e virgola (";"). JavaScript[5] e Perl[6] utilizzano l'operatore virgola allo stesso modo di C/C++. In Java, la virgola è un separatore utilizzato per separare gli elementi in un elenco in vari contesti.[7] Non è un operatore e non valuta l'ultimo elemento nell'elenco.[8]

Note[modifica | modifica wikitesto]

  1. ^ (EN) Comma Operator: ,, su Microsoft dev docs, 8 marzo 2021. URL consultato il 1º agosto 2019.
    «Two expressions separated by a comma are evaluated left to right. The left operand is always evaluated, and all side effects are completed before the right operand is evaluated.»
  2. ^ (EN) SFINAE, su cppreference.com. URL consultato il 9 febbraio 2022.
  3. ^ (EN) Effective Go - for, su go.dev. URL consultato il 9 febbraio 2022.
    «"Finally, Go has no comma operator and ++ and -- are statements not expressions. Thus if you want to run multiple variables in a for you should use parallel assignment (although that precludes ++ and --)."»
  4. ^ (EN) The Go Programming Language Specification - if statements, su go.dev. URL consultato il 9 febbraio 2022.
  5. ^ Comma operator (,), su MDN Web Docs. URL consultato il 25 gennaio 2020.
    «You can use the comma operator when you want to include multiple expressions in a location that requires a single expression.»
  6. ^ Comma Operator, su perldoc.perl.org. URL consultato il 9 febbraio 2022.
  7. ^ 2.4. Grammar Notation, su Oracle Corporation. URL consultato il 25 luglio 2019.
  8. ^ (EN) Is comma (,) operator or separator in Java?, su Stack Overflow, 3 agosto 2016. URL consultato il 9 febbraio 2022.

Bibliografia[modifica | modifica wikitesto]

  • (EN) V. Ramajaran, Computer Programming in C, New Delhi, Prentice Hall of India, 1994.
  • (EN) J.B Dixit, Fundamentals of computers and programming in C, New Delhi, Laxmi Publications, 2005.
  • (EN) Brian W. Kernighan e Dennis M. Ritchie, The C Programming Language, 2ª ed., Englewood Cliffs, NJ, Prentice Hall, 1988.

Voci correlate[modifica | modifica wikitesto]

Collegamenti esterni[modifica | modifica wikitesto]

  Portale Informatica: accedi alle voci di Wikipedia che trattano di informatica