O que o ,
operador faz em C?
O que o ,
operador faz em C?
Respostas:
A expressão:
(expression1, expression2)
A primeira expressão1 é avaliada, depois a expressão2 é avaliada e o valor da expressão2 é retornado para toda a expressão.
i
os valores 5, 4, 3, 2 ou 1. É simplesmente 0. É praticamente inútil, a menos que as expressões tenham efeitos colaterais.
i = b, c;
é equivalente a (i = b), c
porque porque a atribuição =
tem precedência mais alta que o operador de vírgula,
. O operador de vírgula tem a menor precedência de todas.
expression1, expression2;
primeiro expression1
é avaliada, presumivelmente por seus efeitos colaterais (como chamar uma função), depois há um ponto de sequência, depois expression2
é avaliado e o valor retornado ...
Eu já vi usado mais em while
loops:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Ele fará a operação e, em seguida, fará um teste com base em um efeito colateral. A outra maneira seria fazê-lo assim:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. Obviamente, isso não funcionaria se read_string
não tivesse um valor de retorno (ou não tivesse um valor significativo). (Edit: Desculpe, não percebeu quantos anos este post foi.)
while (1)
com uma break;
declaração no corpo. Tentar forçar a parte inicial do código para o teste while ou para o teste do while, geralmente é um desperdício de energia e torna o código mais difícil de entender.
while(1)
e break
;
O operador de vírgula avaliará o operando esquerdo, descartará o resultado e, em seguida, avaliará o operando direito e esse será o resultado. O uso idiomático, conforme observado no link, é ao inicializar as variáveis usadas em um for
loop 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 existem muitos usos excelentes do operador de vírgula , embora seja fácil abusar para gerar código difícil de ler e manter.
No rascunho da norma C99, a gramática é a seguinte:
expression:
assignment-expression
expression , assignment-expression
e o parágrafo 2 diz:
O operando esquerdo de um operador de vírgula é avaliado como uma expressão nula; existe um ponto de sequência após sua avaliação. Então o operando certo é 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 sequência, o comportamento será indefinido.
A nota de rodapé 97 diz:
Um operador de vírgula não gera um valor l .
o que significa que você não pode atribuir ao resultado do operador de vírgula .
É importante observar que o operador de vírgula tem a menor precedência e, portanto, há casos em que o uso ()
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 de vírgula combina as duas expressões de ambos os lados em uma, avaliando-as na ordem da esquerda para a direita. O valor do lado direito é retornado como o valor de toda a expressão.
(expr1, expr2)
é como, { expr1; expr2; }
mas você pode usar o resultado de expr2
uma chamada ou atribuição de função.
É frequentemente visto em for
loops para inicializar ou manter várias variáveis 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, eu só o usei "com raiva" em outro caso, ao encerrar duas operações que devem sempre ir juntas em uma macro. Tínhamos código que copiava vários valores binários em um buffer de bytes para enviar em uma rede e mantinha um ponteiro 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, fizemos o seguinte:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Mais tarde, lemos que esse C não era realmente válido, porque (short *)ptr
não é mais um valor l e não pode ser incrementado, embora nosso compilador na época não se importasse. Para corrigir isso, dividimos a expressão em duas:
*(short *)ptr = short_value;
ptr += sizeof(short);
No entanto, essa abordagem contou com todos os desenvolvedores lembrando de colocar as duas declarações o tempo todo. Queríamos uma função na qual você pudesse passar o ponteiro de saída, o valor ee o tipo do valor. Sendo C, não C ++ com modelos, não poderíamos ter uma função de um tipo arbitrário, por isso decidimos por uma macro:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Ao usar o operador vírgula, pudemos usá-lo em expressões ou como instruções, conforme 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 de bom estilo! Na verdade, eu me lembro do Code Complete de Steve McConnell desaconselhando o uso de operadores de vírgula em um for
loop: para facilitar a leitura e a manutenção, o loop deve ser controlado por apenas uma variável e as expressões na for
própria linha devem conter apenas código de controle de loop, não outros bits extras de inicialização ou manutenção de loop.
Isso causa a avaliação de várias instruções, mas usa apenas a última como um valor resultante (rvalue, 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 único lugar que eu vi ser útil é quando você escreve um loop funky em que deseja fazer várias coisas em uma das expressões (provavelmente a expressão init ou a expressão 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 sintaxe ou se misturei algo que não seja estrito C. Não estou argumentando que o operador é uma boa forma, mas é para isso que você pode usá-lo. No caso acima, eu provavelmente usaria um while
loop para que as múltiplas expressões em init e loop fossem mais óbvias. (E eu inicializaria i1 e i2 inline em vez de declarar e depois inicializar ... blá blá blá.)
Estou revivendo isso simplesmente para responder a perguntas de @Rajesh e @JeffMercado, que eu acho muito importantes, pois esse é um dos principais hits do mecanismo de pesquisa.
Pegue o seguinte trecho de código, por exemplo
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Irá imprimir
1 5
O i
caso é tratado como explicado pela maioria das respostas. Todas as expressões são avaliadas na ordem da esquerda para a direita, mas apenas a última é atribuída i
. O resultado da (
expressão ) is
1`.
O j
caso segue regras de precedência diferentes, pois ,
possui a menor precedência do operador. Devido a essas regras, o compilador vê atribuição-expressão, constante, constante ... . As expressões são novamente avaliadas na ordem da esquerda para a direita e seus efeitos colaterais permanecem visíveis, portanto, j
é o 5
resultado dej = 5
.
De maneira interessante, int j = 5,4,3,2,1;
não é permitido pelas especificações de idioma. Um inicializador espera uma expressão de atribuição para que um ,
operador direto não seja permitido.
Espero que isto ajude.