sizeof tomar dois argumentos
-
27-10-2019 - |
Pergunta
Em C. 1.3 de o C++ É (de 2003.É em C++11 É, também), a norma aponta uma diferença entre a ISO do C e C++;ou seja, para
char arr[100];
sizeof(0, arr)
retorna sizeof(char*)
em C, mas 100
em C++.
Eu posso encontrar nenhuma documentação para sizeof
tomar dois argumentos.O óbvio reversão é o operador da vírgula, mas eu não penso assim: sizeof(arr)
em C é 100
; sizeof(0, arr)
é sizeof(char*)
.Ambos sizeof(0, arr)
e sizeof(arr)
são 100
em C++.
Eu posso estar ausente o ponto o É neste contexto.Alguém pode me ajudar?Isso é semelhante a uma questão discutida em 09, mas ninguém que se refere o É, e eu não acho a resposta correta foi dada.
Editar:Na verdade, o É falar sobre o operador da vírgula.Então, por alguma razão, (0, arr)
retorna um char*
em C, mas um char[100]
em C++.Por quê?
Solução
Em C, então, a matriz está decaindo para um ponteiro, por causa da especificação diferente do operador vírgula em relação a rvalues e lvalues (não é o único lugar onde essa diferença pode ser encontrada).Em C ++, a matriz permanece uma matriz, produzindo o resultado correto.
Outras dicas
Em C, o operador vírgula não produz um lvalue, conseqüentemente o array arr
que é um lvalue decai em um tipo de ponteiro que é um rvalue (neste caso). Portanto, sizeof(0,arr)
torna-se equivalente a sizeof(char*)
, devido à conversão de lvalue-para-rvalue .
Mas em C ++, o operador vírgula produz um lvalue. Não há conversão de lvalue-para-rvalue . Portanto, sizeof(0,arr)
permanece o mesmo, o que é equivalente a sizeof(char[100])
.
A propósito, sizeof
não é uma função, é um operador. Portanto, o seguinte é C ++ completamente válido (e C, se você imaginar printf
em vez de cout
):
int a[100], b[200], c[300], d[400];
cout << sizeof(a,b,c,d) << endl;
Demo: http://www.ideone.com/CtEhn
Você pode pensar que passei 4 operandos para sizeof
, mas isso está errado. sizeof
opera no resultado dos operadores vírgula. E é por causa dos muitos operadores de vírgula que você vê muitos operandos.
4 operandos com 3 operadores vírgulas; assim como em 1+2+3+4
, existem 3 operadores, 4 operandos.
O acima é equivalente ao seguinte (válido em C ++ 0x):
auto & result = (a,b,c,d); //first all comma operators operate on the operands.
cout << sizeof (result) << endl; //sizeof operates on the result
Demo: http://www.ideone.com/07VNf
Portanto, é o operador vírgula que faz você sentir que existem muitos argumentos . Aqui, vírgula é um operador, mas na chamada de função, vírgula NÃO é um operador, é simplesmente um separador de argumento.
function(a,b,c,d); //here comma acts a separator, not operator.
Portanto, sizeof(a,b,c,d)
opera no tipo do resultado dos operadores ,
, exatamente da mesma forma, sizeof(1+2+3+4)
opera no tipo do resultado dos operadores +
.
Observe também que você não pode escrever sizeof(int, char, short)
, precisamente porque o operador vírgula não pode operar em tipos . Ele opera apenas em valor . Acho que sizeof
é o único operador em C e C ++, que pode operar em tipos também. Em C ++, há mais um operador que pode operar em tipos . Seu nome é typeid
.
Ele é um operador da vírgula.E a diferença de que você está falando não tem absolutamente nada a ver com sizeof
.A diferença está realmente no lvalue-para-rvalue, matriz-para-ponteiro e similares decadência comportamentos entre as linguagens C e C++.
A linguagem C é bastante trigger-happy, a este respeito:matrizes de decadência para ponteiros praticamente imediatamente (com a exceção de muito poucos contextos específicos), é por isso que o resultado de 0, arr
a expressão foi char *
escreva.É equivalente a 0, (char *) arr
.
Na linguagem C++ matrizes de preservar a eles "arrayness" muito mais tempo.Quando usada no contexto de ,
operador de matrizes não decaimento para ponteiros (e lvalues não decaimento de rvalues), é por isso que no C++ o tipo de 0, arr
a expressão é ainda char[100]
.
Isto é o que explica a diferença no sizeof
comportamento no exemplo. ?:
o operador é outro exemplo de um operador que demonstra a mesma diferença em decadência comportamento, i.é. sizeof(0 ? arr : arr)
vai lhe dar resultados diferentes em C e C++.Basicamente, tudo o que decorre do fato de que os operadores de C não costumam preservar a lvalueness de seus operandos.Um monte de operadores pode ser usado para demonstrar esse comportamento.
Este não é um sizeof
com dois argumentos.sizeof
é um operador, não uma função.
Considere que (0, arr)
é uma expressão que usa o operador vírgula e todo o resto se encaixa.
sizeof
não aceita dois argumentos. Mas também não é uma função,
então o (...)
não delimita argumentos de função, eles são apenas um
parte opcional da sintaxe e reforça o agrupamento. Quando você escreve
sizeof(0, arr)
, o argumento para sizeof
é a única expressão 0,
arr
. Uma única expressão com um operador vírgula, que avalia o
expressão à esquerda da vírgula, joga fora seu valor (mas não seu
efeitos colaterais) e avalia a expressão à direita da vírgula,
e usa seu valor como o valor da expressão completa.
Não tenho certeza sobre C, mas isso pode ser uma diferença entre o
langauges. Em C ++, a conversão de matriz em ponteiro não ocorre a menos que
é necessário; em C, se bem me lembro, o padrão diz que
sempre ocorre, exceto em certos contextos. Incluindo como o
operador de sizeof
. Neste caso, uma vez que o operador vírgula não
têm restrições com relação aos tipos de seus operandos, o
a conversão de matriz em ponteiro não ocorre em C ++. Em C, um
operatand do operador vírgula não está listado nas exceções, então o
a conversão de matriz em ponteiro ocorre. (Neste caso, a matriz
é um operando do operador vírgula, e não do sizeof
.)
A melhor maneira de ver o que pode estar acontecendo aqui é examinar a gramática do padrão. Se olharmos para o rascunho da seção padrão C99 6.5.3
Operadores unários parágrafo 1 , podemos ver que a gramática para sizeof é:
sizeof unary-expression
sizeof ( type-name )
Portanto, o segundo não se aplica, mas como o sizeof unary-expression
se aplica neste caso? Se olharmos para a seção A.2.1
Expressões do padrão de rascunho e trabalharmos com a gramática da seguinte maneira:
unary-expression -> postfix-expression -> primary-expression -> ( expression )
obtemos os parênteses em torno de uma expressão e agora só temos que olhar para a gramática do operador vírgula da seção 6.5.17
Operador vírgula e nós vemos:
expression:
assignment-expression
expression , assignment-expression
Portanto, agora temos:
sizeof( expression , assignment-expression )
^
|
comma operator
tanto a expressão quanto a expressão de atribuição podem nos levar à expressão primária , que tem a seguinte gramática:
primary-expression:
identifier
constant
string-literal
( expression )
e 0
é uma constante e arr
é um identificador , portanto temos:
sizeof( constant , identifier )
Então, o que o operador vírgula faz aqui? A seção 6.5.17
parágrafo 2 diz:
O operando esquerdo de um operador vírgula é avaliado como uma expressão vazia; existe um ponto de sequência após sua avaliação. Em seguida, o operando certo é avaliado; o resultado tem seu tipo e valor. 97)
uma vez que o operador vírgula não é uma das exceções em que um array não é convertido em um ponteiro, ele produz um ponteiro ( isso é coberto na seção 6.3.2.1
Lvalues, arrays e designadores de função ) o que significa que acabamos com:
sizeof( char * )
Em C ++ a gramática é muito semelhante, então terminamos no mesmo lugar, mas os operadores de vírgula funcionam de forma diferente. O rascunho da seção padrão C ++ 5.18
Operador vírgula diz:
A conversão de[...] O tipo e o valor do resultado são o tipo e o valor do operando certo; o resultado é da mesma categoria de valor de seu operando certo [...]
assim e matriz para ponteiro não é necessária e assim terminamos com:
sizeof( char[100] )
Como vários já disseram, e quero adicionar apenas uma coisa, sizeof é um operador que recebe uma expressão ou uma expressão de conversão. Por esse motivo, adquiri o hábito de escrever parênteses em um tamanho de apenas se for uma expressão de elenco.
char *arr;
struct xxx { ... } v;
Vou escrever
sizeof arr
sizeof v
mas
sizeof (struct xxx) /* Note the space after the sizeof, it's important */
sizeof (char *)
Eu faço o mesmo com return
sem parênteses, pois não é uma chamada de função, e se eu colocar parênteses, é porque a expressão a seguir precisa deles.