Cosa fa l' ,
operatore in C?
Cosa fa l' ,
operatore in C?
Risposte:
L'espressione:
(expression1, expression2)
Viene valutata la prima espressione1, quindi viene valutata expression2 e viene restituito il valore di expression2 per l'intera espressione.
i
mai i valori 5, 4, 3, 2 o 1. È semplicemente 0. È praticamente inutile a meno che le espressioni non abbiano effetti collaterali.
i = b, c;
è equivalente a (i = b), c
perché l'assegnazione =
ha una precedenza maggiore rispetto all'operatore virgola ,
. L'operatore virgola ha la precedenza più bassa di tutte.
expression1, expression2;
prima expression1
viene valutato, presumibilmente per i suoi effetti collaterali (come chiamare una funzione), quindi c'è un punto sequenza, quindi expression2
viene valutato e il valore restituito ...
Ho visto più usato nei while
loop:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Farà l'operazione, quindi eseguirà un test basato su un effetto collaterale. L'altro modo sarebbe farlo in questo modo:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. Ovviamente non funzionerebbe se read_string
non ha un valore di ritorno (o non ha un valore significativo). (Modifica: scusa, non ho notato quanti anni aveva questo post.)
while (1)
con una break;
dichiarazione nel corpo. Cercare di forzare la parte di break-out del codice nel test while o nel test do-while, è spesso uno spreco di energia e rende il codice più difficile da capire.
while(1)
e break
;
L' operatore virgola valuterà l'operando di sinistra, scarterà il risultato e quindi valuterà l'operando di destra e quello sarà il risultato. L' uso idiomatico come indicato nel collegamento è quando si inizializzano le variabili utilizzate in un for
ciclo e fornisce l'esempio seguente:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
Altrimenti non ci sono molti grandi usi dell'operatore virgola , sebbene sia facile abusare di generare codice difficile da leggere e mantenere.
Dalla bozza della norma C99 la grammatica è la seguente:
expression:
assignment-expression
expression , assignment-expression
e il paragrafo 2 dice:
L' operando di sinistra di un operatore virgola viene valutato come espressione nulla; c'è un punto sequenza dopo la sua valutazione. Quindi viene valutato l' operando giusto; il risultato ha il suo tipo e valore. 97) Se si tenta di modificare il risultato di un operatore virgola o di accedervi dopo il successivo punto di sequenza, il comportamento non è definito.
La nota 97 dice:
Un operatore virgola non restituisce un valore .
ciò significa che non è possibile assegnare al risultato dell'operatore virgola .
È importante notare che l'operatore virgola ha la precedenza più bassa e quindi ci sono casi in cui l'utilizzo ()
può fare una grande differenza, ad esempio:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
avrà il seguente output:
1 4
L'operatore virgola combina le due espressioni su entrambi i lati in una, valutandole entrambe nell'ordine da sinistra a destra. Il valore del lato destro viene restituito come valore dell'intera espressione.
(expr1, expr2)
è come { expr1; expr2; }
ma è possibile utilizzare il risultato di expr2
una chiamata di funzione o di un compito.
for
Nei loop si vede spesso inizializzare o mantenere più variabili come questa:
for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
/* do something with low and high and put new values
in newlow and newhigh */
}
A parte questo, l'ho usato solo "con rabbia" in un altro caso, quando si concludono due operazioni che dovrebbero sempre andare insieme in una macro. Avevamo un codice che copiava vari valori binari in un buffer di byte per l'invio su una rete e un puntatore manteneva il punto in cui eravamo arrivati a:
unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff));
Dove erano short
i valori int
abbiamo fatto questo:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
In seguito leggiamo che questo non era veramente valido C, perché (short *)ptr
non è più un valore l e non può essere incrementato, anche se al nostro compilatore non importava. Per risolvere questo problema, abbiamo diviso l'espressione in due:
*(short *)ptr = short_value;
ptr += sizeof(short);
Tuttavia, questo approccio si basava su tutti gli sviluppatori che si ricordavano di inserire entrambe le dichiarazioni in ogni momento. Volevamo una funzione in cui si potesse passare il puntatore di output, il valore e il tipo di valore. Essendo C, non C ++ con template, non potevamo avere una funzione che prendesse un tipo arbitrario, quindi abbiamo optato per una macro:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Usando l'operatore virgola siamo riusciti a usarlo nelle espressioni o come dichiarazioni come desideravamo:
if (need_to_output_short)
ASSIGN_INCR(ptr, short_value, short);
latest_pos = ASSIGN_INCR(ptr, int_value, int);
send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
Non sto suggerendo che nessuno di questi esempi sia di buon stile! In effetti, mi sembra di ricordare il Codice Completo di Steve McConnell che mi consigliava di non utilizzare nemmeno gli operatori virgola in un for
ciclo: per leggibilità e manutenibilità, il ciclo dovrebbe essere controllato da una sola variabile e le espressioni nella for
riga stessa dovrebbero contenere solo codice di controllo del ciclo, non altri bit extra di inizializzazione o manutenzione del loop.
Causa la valutazione di più istruzioni, ma utilizza solo l'ultima come valore risultante (valore, credo).
Così...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
dovrebbe comportare che x sia impostato su 8.
L'unico posto in cui l'ho visto utile è quando scrivi un loop funky in cui vuoi fare più cose in una delle espressioni (probabilmente l'espressione init o l'espressione loop. Qualcosa come:
bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
size_t i1, i2;
for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
{
if(a1[i1] != a2[i2])
{
return false;
}
}
return true;
}
Scusami se ci sono errori di sintassi o se ho mescolato qualcosa che non è rigoroso C. Non sto sostenendo che l'operatore sia in buona forma, ma è per questo che potresti usarlo. Nel caso sopra, probabilmente userei un while
loop, quindi le espressioni multiple su init e loop sarebbero più ovvie. (E inizializzerei i1 e i2 inline invece di dichiarare e quindi inizializzare .... blah blah blah.)
Sto rianimando questo semplicemente per rispondere alle domande di @Rajesh e @JeffMercado che penso siano molto importanti poiché questo è uno dei principali successi dei motori di ricerca.
Prendi ad esempio il seguente frammento di codice
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Stamperà
1 5
Il i
caso viene gestito come spiegato dalla maggior parte delle risposte. Tutte le espressioni vengono valutate nell'ordine da sinistra a destra, ma viene assegnata solo l'ultima i
. Il risultato (
dell'espressione ) is
1`.
Il j
caso segue regole di precedenza diverse poiché ,
ha la precedenza dell'operatore più bassa. A causa di tali norme, il compilatore vede assegnazione-espressione, costante, costante ... . Le espressioni vengono nuovamente valutate nell'ordine da sinistra a destra e i loro effetti collaterali rimangono visibili, pertanto j
è 5
il risultato di j = 5
.
Interessante, int j = 5,4,3,2,1;
non è consentito dalle specifiche della lingua. Un inizializzatore si aspetta un'espressione di assegnazione, quindi ,
non è consentito un operatore diretto .
Spero che questo ti aiuti.