O que o operador de vírgula faz?

O que o operador faz em C?

A expressão:

 (expression1, expression2) 

A primeira expressão1 é avaliada, a expressão2 é avaliada e o valor da expressão2 é retornado para a expressão inteira.

Eu vi usado mais em loops while :

 string s; while(read_string(s), s.len() > 5) { //do something } 

Ele fará a operação e fará um teste baseado em um efeito colateral. A outra maneira seria fazer assim:

 string s; read_string(s); while(s.len() > 5) { //do something read_string(s); } 

O operador de vírgula combina as duas expressões em um lado, avaliando-as na ordem da esquerda para a direita. O valor do lado direito é retornado como o valor da expressão inteira. (expr1, expr2) é como { expr1; expr2; } { expr1; expr2; } { expr1; expr2; } mas você pode usar o resultado de expr2 em uma chamada de function ou atribuição.

Muitas vezes, é visto em for loops para inicializar ou manter várias variables ​​como esta:

 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 */ } 

Além disso, usei apenas "com raiva" em outro caso, quando concluí duas operações que sempre deveriam ser combinadas em uma macro. Nós tínhamos um código que copiava vários valores binários em um buffer de bytes para envio em uma rede, e um ponteiro mantido onde tínhamos chegado:

 unsigned char outbuff[BUFFSIZE]; unsigned char *ptr = outbuff; *ptr++ = first_byte_value; *ptr++ = second_byte_value; send_buff(outbuff, (int)(ptr - outbuff)); 

Onde os valores eram short s ou int s nós fizemos isso:

 *((short *)ptr)++ = short_value; *((int *)ptr)++ = int_value; 

Mais tarde, lemos que isso não era realmente válido C, porque (short *)ptr não é mais um valor l e não pode ser incrementado, embora nosso compilador na época não tenha se importado. Para corrigir isso, dividimos a expressão em dois:

 *(short *)ptr = short_value; ptr += sizeof(short); 

No entanto, essa abordagem dependia de todos os desenvolvedores lembrando-se de colocar as duas instruções o tempo todo. Queríamos uma function onde você pudesse passar o ponteiro de saída, o valor e o tipo do valor. Sendo este C, não C ++ com templates, não poderíamos ter uma function de tipo arbitrário, então nos estabelecemos em uma macro:

 #define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type)) 

Usando o operador vírgula, conseguimos usar isso em expressões ou declarações como desejávamos:

 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)); 

Não estou sugerindo que nenhum desses exemplos seja um bom estilo! De fato, pareço lembrar do Código Completo de Steve McConnell desaconselhando até mesmo a utilização de operadores de vírgula em um loop: para legibilidade e capacidade de manutenção, o loop deve ser controlado por apenas uma variável e as expressões na própria linha devem conter apenas controle de loop código, não outros bits extras de boot ou manutenção de loop.

O operador de vírgula irá avaliar o operando da esquerda, descartar o resultado e então avaliar o operando da direita e esse será o resultado. O uso idiomático, conforme observado no link, é ao inicializar as variables ​​usadas em um loop for e fornece o seguinte exemplo:

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

Caso contrário, não há muitos usos ótimos do operador de vírgula , embora seja fácil abusar para gerar código que é difícil de ler e manter.

A partir do rascunho do padrão C99, a gramática é a seguinte:

 expression: assignment-expression expression , assignment-expression 

e o parágrafo 2 diz:

O operando da esquerda de um operador de vírgula é avaliado como uma expressão vazia; há um ponto de sequência após sua avaliação. Então o operando da direita é avaliado; o resultado tem seu tipo e valor. 97) Se for feita uma tentativa de modificar o resultado de um operador de vírgula ou de acessá-lo após o próximo ponto de seqüência, o comportamento é indefinido.

Nota de rodapé 97 diz:

Um operador de vírgula não produz um lvalue .

o que significa que você não pode atribuir ao resultado do operador vírgula .

É importante observar que o operador vírgula tem a menor precedência e, portanto, há casos em que using () pode fazer uma grande diferença, por exemplo:

 #include  int main() { int x, y ; x = 1, 2 ; y = (3,4) ; printf( "%d %d\n", x, y ) ; } 

terá a seguinte saída:

 1 4 

Isso faz com que a avaliação de várias instruções, mas usa apenas o último como um valor resultante (rvalue, eu acho).

Assim…

 int f() { return 7; } int g() { return 8; } int x = (printf("assigning x"), f(), g() ); 

deve resultar em x sendo definido como 8.

O operador de vírgula não faz nada significativo, é um recurso 100% supérfluo. O principal uso é “pessoas tentando ser inteligentes” e, portanto, usá-lo para (inadvertidamente) ofuscar o código legível. A principal área de uso é ofuscar loops, por exemplo:

 for(int i=0, count=0; i 

Onde int i=0, count=0 na verdade não é o operador vírgula, mas uma lista de declarações (já estamos confusos aqui). i++, count++ é o operador de vírgula, que avalia o operando da esquerda primeiro e depois o da direita. O resultado do operador vírgula é o resultado do operando direito. O resultado do operando esquerdo é descartado.

Mas o código acima poderia ser escrito de uma maneira muito mais legível sem o operador vírgula:

 int count = 0; for(int i=0; i 

O único uso real do operador de vírgula que tenho visto é discussões artificiais sobre pontos de sequência, uma vez que o operador vírgula vem com um ponto de sequência entre a avaliação dos operandos esquerdo e direito.

Então, se você tem algum código de comportamento indefinido como este:

 printf("%d %d", i++, i++); 

Você pode realmente transformá-lo em um comportamento meramente não especificado (ordem de avaliação dos parâmetros de function) escrevendo

 printf("%d %d", (0,i++), (0,i++)); 

Existe agora um ponto de seqüência entre cada avaliação do i++ , portanto, pelo menos, o programa não correrá o risco de travar e queimar por mais tempo, mesmo que a ordem de avaliação dos parâmetros da function não seja especificada.

É claro que ninguém escreveria esse código em aplicativos reais, é útil apenas para discussões de advogados de idiomas sobre pontos de seqüência na linguagem C.

O operador de vírgula é banido por MISRA-C: 2004 e MISRA-C: 2012 com a justificativa de que ele cria código menos legível.

Como respostas anteriores afirmaram, avalia todas as declarações, mas usa a última como o valor da expressão. Pessoalmente eu só achei útil em expressões de loop:

 for (tmp=0, i = MAX; i > 0; i--) 

O único lugar que eu vi ser útil é quando você escreve um loop funky onde você quer fazer várias coisas em uma das expressões (provavelmente a expressão de boot ou expressão de loop. Algo como:

 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; } 

Perdoe-me se houver algum erro de syntax ou se misturei qualquer coisa que não seja rigorosa C. Não estou argumentando que o operador, é de boa forma, mas é para isso que você poderia usá-lo. No caso acima, eu provavelmente usaria um loop while, então as múltiplas expressões no init e no loop seriam mais óbvias. (E eu inicializaria i1 e i2 inline em vez de declarar e depois inicializar ... blá blá blá).