Pergunta

O que faz o , operador fazer em C?

Foi útil?

Solução

A expressão:

(expression1,  expression2)

Primeiro expressão1 é avaliada e, em seguida, expressão2 é avaliada, e o valor de expressão2 é retornado para a expressão inteira.

Outras dicas

Eu vi mais utilizados na while loops:

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

Ele vai fazer a operação e, em seguida, fazer um teste baseado em um efeito colateral.A outra forma seria a de fazer como esta:

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

O operador da vírgula irá avaliar o operando esquerdo, descartar o resultado e, em seguida, avaliar o operando da direita e que vai ser o resultado.O idiomáticas use como indicado no link ao inicializar as variáveis utilizadas em um for loop, e ele dá 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 grande usos da operador da vírgula, apesar de fácil abuso para gerar o código que é difícil de ler e manter.

Do projecto de padrão C99 a gramática é como segue:

expression:
  assignment-expression
  expression , assignment-expression

e parágrafo 2 diz:

O operando esquerdo de um operador da vírgula é avaliada como um vazio de expressão; há um ponto de seqüência, após a sua avaliação.Em seguida, o direito operando é avaliado;o resultado tem o seu tipo e o valor. 97) Se for feita uma tentativa para modificar o resultado de uma vírgula operador ou para o acesso após o próximo ponto de seqüência, o comportamento é indefinido.

Nota de rodapé 97 diz:

Um operador da vírgula não não produzir um lvalue.

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

É importante notar que a vírgula e do operador menor precedência, e, portanto, existem casos onde usar () pode fazer uma grande diferença, por exemplo:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

terá a seguinte saída:

1 4

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

Ele é muitas vezes visto em for loops para iniciar ou manter múltiplas variáveis, como este:

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

Aparte isso, eu só usei "raiva" em um outro caso, quando o acondicionamento de duas operações que devem sempre andar juntos em uma macro.Tivemos código que copiou vários valores binários em um buffer de bytes para o envio de uma rede, e um ponteiro mantida onde tínhamos chegado até:

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 foram shorts ou ints fizemos isso:

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

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

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

No entanto, esta abordagem invocado todos os desenvolvedores de lembrar de colocar as duas afirmações em todo o tempo.Queríamos uma função onde você pode passar o ponteiro saída, o valor e o tipo do valor.Esta sendo C, C++ com templates, que não poderia ter uma função de levar um tipo arbitrário, então, baseamo-nos em uma macro:

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

Usando o operador da vírgula fomos capazes de usar isso em expressões ou instruções de como nós queria:

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

Eu não estou sugerindo que qualquer um desses exemplos são bons estilo!De fato, eu me lembro de Steve McConnell Código Completo desaconselha mesmo usando vírgula operadores em uma for loop:para a legibilidade e facilidade de manutenção, o ciclo deve ser controlado por apenas uma variável, e as expressões dos for linha em si, deve conter apenas de controle do loop de código, e não a outras bits extras de inicialização ou do circuito de manutenção.

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

Então...

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 da vírgula não faz nada significativo, é um 100% supérfluo recurso.O uso principal é "pessoas tentando ser inteligente" e, portanto, usá-lo para (não intencional) ofuscar um código legível.A área de maior utilização é para ofuscar loops, por exemplo:

for(int i=0, count=0; i<x; i++, count++)

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

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

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

Só o real uso do operador da vírgula que eu tenho visto, é artificial discussões sobre a sequência de pontos, desde que o operador da vírgula vem com um ponto de seqüência entre a avaliação de que a esquerda e para a direita operandos.

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

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

Você pode realmente transformá-lo em mero comportamento indeterminado (ordem de avaliação dos parâmetros de função) por escrito

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

Há agora um ponto de seqüência entre cada avaliação de i++, pelo menos, o programa não o risco de bater e queimar mais, mesmo que a ordem de avaliação dos parâmetros de função permanece não especificado.

É claro que ninguém iria escrever esse código em aplicações reais, é somente útil para o idioma advogado discussões sobre a sequência de pontos na linguagem C.

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

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

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

O único lugar que eu vi ele ser útil é quando você escrever um funky ciclo em que você deseja fazer várias coisas em uma das expressões (provavelmente o init expressão ou loop de expressão.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 há erros de sintaxe ou se eu misturados em qualquer coisa que não seja estrita C.Eu não estou argumentando que o operador , é uma boa forma, mas é o que você poderia usá-lo para.No caso acima eu provavelmente usar um while loop em vez disso, para as múltiplas expressões de inicialização e de ciclo seria mais óbvio.(E eu inicializar i1 e i2 inline em vez de declarar e inicializar....blá, blá, blá.)

Estou revivendo este simplesmente para responder a questões a partir @Rajesh e @JeffMercado que eu acho muito importante, pois este é um dos principais motor de busca de acertos.

Tome o seguinte fragmento de código de exemplo

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

Ele vai imprimir

1 5

O i o caso é tratado como explicado por mais respostas.Todas as expressões são avaliadas da esquerda para direita, mas só a última é atribuída a i.O resultado da ( expressão )is1`.

O j caso segue diferentes regras de precedência desde , a que tem menor precedência de operador.Por causa dessas regras, o compilador vê atribuição-expressão, constante, constante ....As expressões são novamente avaliados da esquerda para direita e de seus efeitos colaterais ficar visível, portanto, j é 5 como resultado de j = 5.

Interstingly, int j = 5,4,3,2,1; não é permitida pela linguagem de especificação.Um inicializador espera um atribuição-expressão de forma direta , operador não é permitido.

Espero que isso ajude.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top