Eu tenho este programa simples:
#include <stdio.h>
struct S
{
int i;
};
void swap(struct S *a, struct S *b)
{
struct S temp;
temp = *a /* Oops, missing a semicolon here... */
*a = *b;
*b = temp;
}
int main(void)
{
struct S a = { 1 };
struct S b = { 2 };
swap(&a, &b);
}
Conforme visto em, por exemplo, ideone.com, isso dá um erro:
prog.c: In function 'swap': prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *') *a = *b; ^
Por que o compilador não detecta o ponto-e-vírgula ausente?
Nota: Esta pergunta e sua resposta são motivadas por esta pergunta . Embora existam outras questões semelhantes a esta, não encontrei nada mencionando a capacidade de forma livre da linguagem C, que é o que está causando isso e erros relacionados.
Respostas:
C é uma linguagem de forma livre . Isso significa que você pode formatá-lo de várias maneiras e ainda será um programa legal.
Por exemplo, uma declaração como
poderia ser escrito como
ou como
Então, quando o compilador vê as linhas
pensa que significa
Obviamente, essa não é uma expressão válida e o compilador reclamará disso em vez da falta do ponto-e-vírgula. A razão de não ser válido é porque
a
é um ponteiro para uma estrutura, portanto,*a * a
está tentando multiplicar uma instância de estrutura (*a
) com um ponteiro para uma estrutura (a
).Embora o compilador não consiga detectar o ponto-e-vírgula ausente, ele também relata o erro totalmente não relacionado na linha errada. É importante notar isso porque não importa o quanto você olhe para a linha onde o erro é relatado, não há erro lá. Às vezes, problemas como esse exigirão que você examine as linhas anteriores para ver se estão corretas e sem erros.
Às vezes, você ainda precisa procurar em outro arquivo para encontrar o erro. Por exemplo, se um arquivo de cabeçalho está definindo uma estrutura da última vez que define no arquivo de cabeçalho, e o ponto-e-vírgula que encerra a estrutura está ausente, o erro não estará no arquivo de cabeçalho, mas no arquivo que inclui o arquivo de cabeçalho.
E às vezes fica ainda pior: se você incluir dois (ou mais) arquivos de cabeçalho, e o primeiro contiver uma declaração incompleta, muito provavelmente o erro de sintaxe será indicado no segundo arquivo de cabeçalho.
Relacionado a isso está o conceito de erros de acompanhamento . Alguns erros, normalmente devido à falta de ponto-e-vírgula, são relatados como múltiplos erros . É por isso que é importante começar do início ao corrigir os erros, pois corrigir o primeiro erro pode fazer com que vários erros desapareçam.
Isso, é claro, pode levar à correção de um erro por vez e recompilações frequentes, o que pode ser complicado em projetos grandes. Porém, reconhecer esses erros de acompanhamento é algo que vem com a experiência e, depois de vê-los algumas vezes, é mais fácil descobrir os erros reais e corrigir mais de um erro por recompilação.
fonte
temp = *a * a = *b
poderia ser uma expressão válida seoperator*
estivesse sobrecarregada. (A pergunta está marcada como “C”, no entanto.)Existem três coisas para lembrar.
*
em C pode ser um operador unário e binário. Como um operador unário, significa "desreferenciar", como um operador binário significa "multiplicar".O resultado desses dois fatos é quando analisamos.
O primeiro e o último
*
são interpretados como unários, mas o segundo*
é interpretado como binário. De uma perspectiva de sintaxe, isso parece OK.É apenas após a análise, quando o compilador tenta interpretar os operadores no contexto de seus tipos de operando, que um erro é visto.
fonte
Algumas boas respostas acima, mas vou elaborar.
Na verdade, este é um caso em
x = y = z;
quex
ey
são atribuídos o valor dez
.O que você está dizendo é
the contents of address (a times a) become equal to the contents of b, as does temp
.Em suma,
*a *a = <any integer value>
é uma declaração válida. Como apontado anteriormente, o primeiro*
desreferencia um ponteiro, enquanto o segundo multiplica dois valores.fonte
y
nem é uma variável, é a expressão*a *a
, e você não pode atribuir ao resultado de uma multiplicação.A maioria dos compiladores analisa os arquivos de origem em ordem e relata a linha onde descobrem que algo estava errado. As primeiras 12 linhas de seu programa C podem ser o início de um programa C válido (sem erros). As primeiras 13 linhas do seu programa não podem. Alguns compiladores notarão a localização das coisas que encontram que não são erros em si mesmas e, na maioria dos casos, não disparam erros posteriormente no código, mas podem não ser válidos em combinação com outra coisa. Por exemplo:
A declaração
int foo;
por si só seria perfeitamente adequada. Da mesma forma a declaraçãofloat foo;
. Alguns compiladores podem registrar o número da linha onde a primeira declaração apareceu e associar uma mensagem informativa a essa linha, para ajudar o programador a identificar os casos em que a definição anterior é, na verdade, a errada. Os compiladores também podem manter os números de linha associados a algo como umdo
, que pode ser relatado se o associadowhile
não aparecer no lugar certo. Para casos em que a localização provável do problema seria imediatamente anterior à linha em que o erro foi descoberto, entretanto, os compiladores geralmente não se preocupam em adicionar um relatório extra para a posição.fonte