Por que sizeof int está errado, enquanto sizeof (int) está certo?

96

Sabemos que sizeofé um operador usado para calcular o tamanho de qualquer tipo de dado e expressão, e quando o operando é uma expressão, os parênteses podem ser omitidos.

int main()
{
        int a;

        sizeof int;
        sizeof( int );
        sizeof a;
        sizeof( a );

        return 0;
}

o primeiro uso de sizeofestá errado, enquanto outros estão certos.

Quando ele é compilado usando gcc, a seguinte mensagem de erro será fornecida:

main.c:5:9: error: expected expression before int

Minha dúvida é por que o padrão C não permite esse tipo de operação. Vai sizeof intcausar alguma ambigüidade?

Yishu Fang
fonte
5
O engraçado é que nem todas as expressões são aceitas sem parênteses: tente sizeof (int)a.
Fred Foo
2
@MikkelK .: OP está perguntando por trás das aspas padronizadas, ele já conhece as aspas que são mencionadas na resposta marcada.
Alok Save
2
@Lundin: sizeof +(int)atem a vantagem *&de realmente compilar ;-)
Steve Jessop
2
@SteveJessop Ah certo, não é um lvalue. Embora tenha compilado em Embarcadero C ++, por incrível que pareça. De qualquer forma, acredito que acabamos de encontrar um uso para o operador + unário! Essa deve ser a primeira vez na história da programação C :)
Lundin
2
@Lundin: Você ainda tem que ter cuidado com o unário +. Por exemplo, sizeof +(char)a == sizeof(int)devido à promoção de inteiros, é provavelmente menos sujeito a erros apenas colocar entre parênteses se houver alguma preocupação sobre a expressão da qual você está tentando calcular o tamanho. Então, eu não tenho certeza se chegaria a chamá-lo de "um uso", embora admita que o usei ...
Steve Jessop

Respostas:

101

O seguinte pode ser ambíguo:

sizeof int * + 1

É isso (sizeof (int*)) + 1ou (sizeof(int)) * (+1)?

Obviamente, a linguagem C poderia ter introduzido uma regra para resolver a ambigüidade, mas posso imaginar por que isso não incomodou. Com a linguagem como está, um especificador de tipo nunca aparece "nu" em uma expressão e, portanto, não há necessidade de regras para determinar se esse segundo *é parte do tipo ou um operador aritmético.

A gramática existente já resolve a ambigüidade potencial de sizeof (int *) + 1. É (sizeof(int*))+1, não sizeof((int*)(+1)) .

C ++ tem um problema um pouco semelhante para resolver com a sintaxe de conversão de estilo de função. Você pode escrever int(0)e pode escrever typedef int *intptr; intptr(0);, mas não pode escrever int*(0). Nesse caso, a resolução é que o tipo "simples" deve ser um nome de tipo simples, não pode ser apenas qualquer id de tipo antigo que possa conter espaços ou pontuação à direita. Talvez sizeofpudesse ter sido definido com a mesma restrição, não tenho certeza.

Steve Jessop
fonte
1
C ++ tem o new int*(X)que seria ambíguo se C ++ não especificasse que o novo operador pega a string mais longa que poderia ser um tipo. Então, em C ++, eles poderiam ter feito o mesmo com sizeof, eu acho. Mas em C ++ você pode dizer sizeof int()qual seria ambíguo (tipo ou valor inicializado int?).
Johannes Schaub - litb
@ JohannesSchaub-litb: Mmm, então talvez C ++ pudesse dizer que o tipo é preferido se for possível analisar um, caso contrário, é uma expressão. A ambigüidade seria resolvida, mas sizeof int()seria malformada ("não operator()para size_t"), o que eu acho que seria indesejável!
Steve Jessop
32

Do padrão C99

6.5.3.4.2
O sizeofoperador retorna o tamanho (em bytes) de seu operando, que pode ser uma expressão ou o nome de um tipo entre parênteses.

No seu caso, intnão é uma expressão nem um nome entre parênteses.

Jainendra
fonte
10
Eu não entendo por que isso recebe 7 votos positivos. Como as três respostas excluídas, ele apenas repete a regra em vez de explicar por que a regra existe.
Fred Foo
7
@larsmans, sim, concordo com você. Embora continue a repetir a regra, mas pelo menos, dá um pedaço de leitura autorizadora.
Yishu Fang
3
@larsmans Se começarmos a tentar justificar todas as decisões tomadas em C99, estaremos aqui o ano todo. Citar o padrão é uma maneira tão boa quanto qualquer outra de encerrar esta discussão.
Perry
1
@Perry: se você não gosta desse tipo de questão, você pode votar para encerrar a questão como argumentativo.
Fred Foo
4
Como um não praticante de C ++, mesmo eu entendo esta resposta, não tenho certeza qual é o problema se você pode ler e compreender inglês.
Kev
6

Existem duas maneiras de usar o operador sizeof em C. A sintaxe é esta:

C11 6.5.3 Unary operators
...
sizeof unary-expression
sizeof ( type-name )

Sempre que usar um tipo como operando, deve-se ter o parêntese, pela definição da sintaxe da linguagem. Se você usar sizeof em uma expressão, não precisará do parêntese.

O padrão C dá um exemplo de onde você pode querer usá-lo em uma expressão:

sizeof array / sizeof array[0]

No entanto, por uma questão de consistência e para evitar bugs relacionados à precedência do operador, eu pessoalmente aconselharia sempre usar (), não importa a situação.

Lundin
fonte
@ JohannesSchaub-litb Concordo que não fornece nenhuma justificativa para como o comitê de padrão C raciocinou quando especificou o padrão C90. Você teria que perguntar a eles ... Ele responde sem nenhuma justificativa fornecida, no entanto, C não permite, pois a sintaxe é conforme especificado em 6.5.3. O OP também parecia desconhecer a diferença entre sizeof expressione sizeof(type), o que é explicado nesta resposta.
Lundin,
(Para registro, acabei de ler os fundamentos do C90 e C11 e nenhum fornece qualquer explicação.)
Lundin
Eu suspeito que não haja nenhuma justificativa necessária para isso, é apenas a maneira como os designers C originais decidiram fazer isso. Talvez tenha facilitado as coisas para o analisador inicial. Eu postaria uma resposta sobre o fato de que typedefs e variáveis ​​compartilham o mesmo namespace, mas isso não impede a permissão da primeira sintaxe; ele só precisa das regras de análise para dizer que prefere a variável quando não há parênteses, prefere o tipo quando há, e qualquer forma pode ser usada quando o nome não é ambíguo. Como não há necessidade de alterá-lo, os comitês de padrões deixaram de lado.
Barmar