Perdoe se esta pergunta é ingênua. Considere o seguinte programa:
#include <stdio.h>
int main() {
int i = 1;
i = i + 2;
5;
i;
printf("i: %d\n", i);
}
No exemplo acima, as demonstrações 5;
e i;
parece totalmente supérfluo, mas os compila código sem avisos ou erros por padrão (no entanto, gcc faz lançar um warning: statement with no effect [-Wunused-value]
aviso quando correu com -Wall
). Eles não têm efeito sobre o restante do programa, então por que são considerados declarações válidas em primeiro lugar? O compilador simplesmente os ignora? Existem benefícios em permitir tais declarações?
;
depois dela. Seria complicado para o idioma adicionar mais regras sobre quando expressões não podem ser declaraçõesprintf()
? A instrução5;
basicamente diz "faça o que5
fizer (nada) e ignore o resultado. Sua instruçãoprintf(...)
é" faça o queprintf(...)
fizer e ignore os resultados (o valor de retorno deprintf()
) ". C trata os mesmos. Isso também permite códigos como(void) i;
ondei
está um parâmetro para uma função que você projeta paravoid
marcá-lo como deliberadamente não utilizadoprintf()
tem um efeito, mesmo que você ignore o valor que eventualmente retorna. Por outro lado5;
, não tem efeito algum.Respostas:
Um benefício em permitir tais declarações é do código criado por macros ou outros programas, em vez de ser escrito por humanos.
Como exemplo, imagine uma função
int do_stuff(void)
que deve retornar 0 em caso de sucesso ou -1 em caso de falha. Pode ser que o suporte a "coisas" seja opcional e, portanto, você possa ter um arquivo de cabeçalho queAgora imagine um código que deseja fazer coisas, se possível, mas pode ou não se importar se é bem-sucedido ou falha:
Quando
STUFF_SUPPORTED
for 0, o pré-processador expandirá a chamadafunc2
para uma instrução que apenas lêe assim o passe do compilador verá exatamente o tipo de declaração "supérflua" que parece incomodá-lo. No entanto, o que mais se pode fazer? Se você
#define do_stuff() // nothing
, o códigofunc1
será quebrado. (E você ainda terá uma declaração vazia nafunc2
qual apenas lê;
, o que talvez seja ainda mais supérfluo.) Por outro lado, se você precisar realmente definir umado_stuff()
função que retorne -1, poderá incorrer no custo de uma chamada de função por nenhuma boa razão.fonte
((void)0)
.assert
.Instruções simples em C são encerradas por ponto e vírgula.
Instruções simples em C são expressões. Uma expressão é uma combinação de variáveis, constantes e operadores. Toda expressão resulta em algum valor de um determinado tipo que pode ser atribuído a uma variável.
Dito isto, alguns "compiladores inteligentes" podem descartar 5; e eu; afirmações.
fonte
void
não tem valor.Declarações sem efeito são permitidas porque seria mais difícil bani-las do que permitir. Isso foi mais relevante quando o C foi projetado pela primeira vez e os compiladores eram menores e mais simples.
Uma declaração de expressão consiste em uma expressão seguida por ponto e vírgula. Seu comportamento é avaliar a expressão e descartar o resultado (se houver). Normalmente, o objetivo é que a avaliação da expressão tenha efeitos colaterais, mas nem sempre é fácil ou mesmo possível determinar se uma determinada expressão tem efeitos colaterais.
Por exemplo, uma chamada de função é uma expressão; portanto, uma chamada de função seguida por um ponto e vírgula é uma instrução. Esta declaração tem algum efeito colateral?
É impossível dizer sem ver a implementação de
some_function
.Que tal agora?
Provavelmente não - mas se
obj
é definido comovolatile
, então ele faz.Permitir que qualquer expressão seja transformada em uma declaração de expressão adicionando um ponto-e-vírgula torna a definição da linguagem mais simples. Exigir que a expressão tenha efeitos colaterais adicionaria complexidade à definição da linguagem e ao compilador. C é construído sobre um conjunto consistente de regras (chamadas de função são expressões, atribuições são expressões, uma expressão seguida por ponto-e-vírgula é uma declaração) e permite que os programadores façam o que desejam, sem impedir que façam coisas que possam ou não fazer sentido.
fonte
As instruções listadas sem efeito são exemplos de uma expressão , cuja sintaxe é fornecida na seção 6.8.3p1 do padrão C , da seguinte maneira:
Toda a seção 6.5 é dedicada à definição de uma expressão, mas, em termos gerais, uma expressão consiste em constantes e identificadores vinculados aos operadores. Notavelmente, uma expressão pode ou não conter um operador de atribuição e pode ou não conter uma chamada de função.
Portanto, qualquer expressão seguida de ponto e vírgula se qualifica como uma declaração de expressão. De fato, cada uma dessas linhas do seu código é um exemplo de uma declaração de expressão:
Alguns operadores contêm efeitos colaterais, como o conjunto de operadores de atribuição e os operadores de aumento / redução anteriores / posteriores, e o operador de chamada de função
()
pode ter um efeito colateral, dependendo do que a função em questão faz. No entanto, não é necessário que um dos operadores tenha um efeito colateral.Aqui está outro exemplo:
Isso está chamando uma função e descartando o resultado, assim como a chamada
printf
no seu exemplo, mas o contrárioprintf
da chamada de função em si não tem um efeito colateral.fonte
Às vezes, essas declarações são muito úteis:
Ou quando o manual de referência nos diz para ler apenas os registros para arquivar algo - por exemplo, para limpar ou definir alguma sinalização (situação muito comum no mundo dos EUA)
https://godbolt.org/z/6wjh_5
fonte
*SREG
é volátil,*SREG;
não tem efeito no modelo especificado pelo padrão C. O padrão C especifica que ele tem um efeito colateral observável.