Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Existem casos em que atribuições aninhadas dentro de uma condição podem realmente tornar um programa mais fácil de ler. Aqui está um exemplo usando um tipo hipotético list
que representa uma lista; ele testa se a lista tem pelo menos dois elementos, utilizando funções hipotéticas, nonempty
, que retorna verdadeiro se o argumento for uma lista não vazia, e list_next
, que avança de um elemento da lista para o próximo. Assumimos que uma lista nunca é um ponteiro nulo, de modo que as expressões de atribuição são sempre “verdadeiras.”
Aqui aproveitamos o operador ‘&&’ para evitar a execução do restante do código se uma chamada a nonempty
retornar “falso.” O único lugar natural para colocar as atribuições é entre essas chamadas.
Seria possível reescrever isso como várias instruções, mas isso poderia tornar o código muito mais pesado. Por outro lado, quando o teste é ainda mais complexo do que este, dividi-lo em várias instruções pode ser necessário para manter a clareza.
Se uma lista vazia for um ponteiro nulo, podemos dispensar a chamada a nonempty
:
Este capítulo descreve os operadores de C que combinam expressões para controlar quais dessas expressões serão executadas, ou seja, em qual ordem.
O operador vírgula representa a execução sequencial de expressões. O valor da expressão com vírgula vem da última expressão da sequência; as expressões anteriores são calculadas apenas por seus efeitos colaterais. Ele se parece com isto:
Você pode agrupar qualquer número de expressões dessa forma, colocando vírgulas entre elas.
Com vírgulas, você pode colocar várias expressões em um lugar que exige apenas uma expressão — por exemplo, no cabeçalho de uma instrução for
. Esta instrução
contém três expressões de atribuição para inicializar i
, j
e k
. A sintaxe de for
exige apenas uma expressão para inicialização; para incluir três atribuições, usamos vírgulas para agrupá-las em uma única expressão maior: i = 0, j = 10, k = 20
. Essa técnica também é útil na expressão de avanço do laço, a última das três dentro dos parênteses do for
.
Na instrução for
e na instrução while
(veja Instruções de Laço), uma vírgula fornece uma maneira de realizar algum efeito colateral antes do teste de saída do laço. Por exemplo,
Sempre escreva parênteses ao redor de uma série de operadores vírgula, exceto quando estiverem no nível superior em uma instrução de expressão, ou dentro dos parênteses de uma instrução if
, for
, while
ou switch
(veja ). Por exemplo, em
as vírgulas entre as atribuições são claras porque estão entre um parêntese e um ponto e vírgula.
Os argumentos em uma chamada de função também são separados por vírgulas, mas isso não é um caso do operador vírgula. Note a diferença entre
que passa três argumentos para foo
e
que usa o operador vírgula e passa apenas um argumento (com valor 6).
Aviso: não use o operador vírgula ao redor de um argumento de função, a menos que isso torne o código mais legível. Quando o fizer, não coloque parte de outro argumento na mesma linha. Em vez disso, adicione uma quebra de linha para tornar os parênteses ao redor do operador vírgula mais fáceis de ver, como neste exemplo:
Você pode usar uma vírgula em qualquer subexpressão, mas, na maioria dos casos, isso apenas torna o código confuso, e é mais claro elevar todas as expressões separadas por vírgula, exceto a última, para um nível mais alto. Assim, em vez disso:
é muito mais claro escrever assim:
ou assim:
Use vírgulas apenas nos casos em que não haja alternativa mais clara envolvendo múltiplas instruções.
Por outro lado, não hesite em usar vírgulas na expansão de uma definição de macro. As compensações em termos de clareza de código são diferentes nesse caso, porque o uso da macro pode melhorar tanto a clareza geral que a "feiúra" da definição da macro é um pequeno preço a pagar. Veja .
Os operadores lógicos combinam valores verdade (verdadeiro ou falso), que normalmente são representados em C como números. Qualquer expressão com um valor numérico é um valor verdade válido: zero significa falso, e qualquer outro valor significa verdadeiro. Um tipo de ponteiro também é significativo como valor verdade; um ponteiro nulo (que é zero) significa falso, e um ponteiro não nulo significa verdadeiro (veja ). O valor de um operador lógico é sempre 1 ou 0 e tem o tipo int
(veja ).
Os operadores lógicos são usados principalmente na condição de uma instrução if
, ou no teste final de uma instrução for
ou while
(veja ). No entanto, eles são válidos em qualquer contexto onde uma expressão com valor inteiro seja permitida.
Operador unário para "não" lógico. O valor é 1 (verdadeiro) se exp
for 0 (falso), e 0 (falso) se exp
for diferente de zero (verdadeiro).
Aviso: se exp
for qualquer coisa além de um lvalue ou uma chamada de função, você deve escrever parênteses ao redor dela.
O operador binário "e" lógico computa left
e, se necessário, right
. Se ambos os operandos forem verdadeiros, a expressão &&
retorna o valor 1 (verdadeiro). Caso contrário, a expressão &&
retorna o valor 0 (falso). Se left
resultar em um valor falso, isso determina o resultado geral, então right
nem é computado.
O operador binário "ou" lógico computa left
e, se necessário, right
. Se pelo menos um dos operandos for verdadeiro, a expressão ||
retorna o valor 1 (verdadeiro). Caso contrário, a expressão ||
retorna o valor 0 (falso). Se left
resultar em um valor verdadeiro, isso determina o resultado geral, então right
nem é computado.
Aviso: nunca confie na precedência relativa de &&
e ||
. Quando você usá-los juntos, sempre use parênteses para especificar explicitamente como eles se aninham, como mostrado aqui:
A coisa mais comum a se usar dentro dos operadores lógicos é uma comparação. Convenientemente, ‘&&’ e ‘||’ têm precedência mais baixa do que os operadores de comparação e operadores aritméticos, então podemos escrever expressões assim, sem parênteses, e obter o aninhamento que é natural: duas operações de comparação que devem ser ambas verdadeiras.
Este exemplo também mostra como é útil que ‘&&’ garanta pular o operando à direita se o operando à esquerda for falso. Por causa disso, esse código nunca tenta dividir por zero.
Isto é equivalente:
Um valor verdade é simplesmente um número, então usar r
como valor verdade testa se ele é diferente de zero. Mas o significado de r
como uma expressão não é um valor verdade — é um número a ser usado na divisão. Portanto, é mais estiloso escrever explicitamente != 0
.
Aqui está outra maneira equivalente de escrever isso:
Isso ilustra o operador unário ‘!’, e a necessidade de escrever parênteses ao redor de seu operando.
C possui uma expressão condicional que seleciona uma de duas expressões para calcular e obter o valor. Ela se parece com isto:
O primeiro operando, condição
, deve ser um valor que possa ser comparado com zero — um número ou um ponteiro. Se for verdadeiro (diferente de zero), a expressão condicional calcula se_verdadeiro
e seu valor se torna o valor da expressão condicional. Caso contrário, a expressão condicional calcula se_falso
e seu valor se torna o valor da expressão condicional. A expressão condicional sempre calcula apenas uma das duas, se_verdadeiro
ou se_falso
, nunca ambas.
Aqui está um exemplo: o valor absoluto de um número x
pode ser escrito como:
Aviso: Os operadores de expressão condicional têm uma precedência sintática relativamente baixa. Exceto quando a expressão condicional é usada como argumento em uma chamada de função, escreva parênteses ao redor dela. Para maior clareza, sempre escreva parênteses ao redor se ela se estender por mais de uma linha.
Os operadores de atribuição e o operador vírgula (veja ) têm precedência mais baixa do que os operadores de expressão condicional, então coloque parênteses ao redor deles quando aparecerem dentro de uma expressão condicional. Veja .
Chamamos os ramos (branches) da expressão condicional de se_verdadeiro e se_falso.
Os dois ramos normalmente devem ter o mesmo tipo, mas algumas exceções são permitidas. Se ambos forem tipos numéricos, a expressão condicional converte ambos para o tipo comum (veja ).
Com ponteiros (veja ), os dois valores podem ser ponteiros para tipos minimamente compatíveis (veja ). Nesse caso, o tipo de resultado é um ponteiro semelhante, cujo tipo de destino combina todos os qualificadores de tipo (veja ) de ambos os ramos.
Se um dos ramos tiver o tipo void *
e o outro for um ponteiro para um objeto (não para uma função), a expressão condicional converte o ramo void *
para o tipo do outro.
Se um dos ramos for uma constante inteira com valor zero e o outro for um ponteiro, a expressão condicional converte zero para o tipo do ponteiro.
No GNU C, você pode omitir se_verdadeiro em uma expressão condicional. Nesse caso, se a condição for diferente de zero, seu valor se torna o valor da expressão condicional, após a conversão para o tipo comum. Assim,
tem o valor de x
se for diferente de zero; caso contrário, o valor de y
.
Omitir se_verdadeiro é útil quando a condição tem efeitos colaterais. Nesse caso, escrever a expressão duas vezes executaria os efeitos colaterais duas vezes, mas escrevê-la uma vez os executa apenas uma vez. Por exemplo, supondo que a função next_element
avance uma variável ponteiro para apontar para o próximo elemento em uma lista e retorne o novo ponteiro,
é uma maneira de avançar o ponteiro e usar seu novo valor, se ele não for nulo, mas usar default_pointer
se for nulo. Não podemos fazer da seguinte forma:
porque isso avançaria o ponteiro uma segunda vez.