Por que sizeof (x ++) não incrementa x?

505

Aqui está o código compilado nas janelas dev c ++:

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d and ", sizeof(x++)); // note 1
    printf("%d\n", x); // note 2
    return 0;
}

Espero xser 6 depois de executar a nota 1 . No entanto, a saída é:

4 and 5

Alguém pode explicar por xque não aumenta após a nota 1 ?

Neigyl R. Noval
fonte
37
Eu notar que DevC ++ usa um compilador ultrapassada muito antiga, você pode querer atualizar para uma IDE mais recente, por exemplo, Eclipse codeblocks ou Visual Studio
Tom J Nowell
4
++ x produz o mesmo resultado que x ++, cygwin e gcc 3.4.4.
yehnan
6
@ Tim Pletzcker Porque o primeiro valor é o tamanho da variável e não a própria variável.
Surfbutler
Minha resposta foi adicionada devido a uma mesclagem com essa pergunta. Na verdade, minha resposta mostra como você pode obter uma avaliação em tempo de execução no caso de VLAsnenhuma das outras.
Shafik Yaghmour
2
esse tipo de escrita deve ser evitado, pois pode causar efeitos colaterais indesejáveis. Sua pergunta já é uma delas.
User666412

Respostas:

537

Do padrão C99 (a ênfase é minha)

6.5.3.4/2

O operador sizeof gera o tamanho (em bytes) de seu operando, que pode ser uma expressão ou o nome entre parênteses de um tipo. O tamanho é determinado a partir do tipo do operando. O resultado é um número inteiro. Se o tipo do operando for um tipo de matriz de comprimento variável, o operando será avaliado; caso contrário, o operando não é avaliado e o resultado é uma constante inteira.

pmg
fonte
51
"Se o tipo do operando for um tipo de matriz de comprimento variável, o operando será avaliado" uau! Eu nunca percebi que
Kos
6
o que você quer dizer com tipo de matriz de comprimento variável? Isso significa que o operando é uma matriz? O código nesse caso não é uma matriz. Você pode esclarecer as coisas para mim?
Neigyl R. Noval 22/11
37
Uma matriz de comprimento variável é uma matriz declarada com o tamanho sendo um valor desconhecido durante a compilação, por exemplo, se você ler Nem stdin e make int array[N]. Este é um dos recursos do C99, indisponível no C ++.
Kos
21
@LegendofCage, em particular, isso significaria que, em algo como sizeof(int[++x])(realmente, realmente uma péssima idéia), ele ++poderia ser avaliado.
Jens Gustedt
3
@ Joe Wreschnig: Ele é avaliada por gcc, clange em ideone.com/Pf7iF
JFS
190

sizeofé um operador em tempo de compilação, portanto, no momento da compilação sizeofe seu operando, é substituído pelo valor do resultado. O operando não é avaliado (exceto quando é uma matriz de comprimento variável); apenas o tipo do resultado é importante.

short func(short x) {  // this function never gets called !!
   printf("%d", x);    // this print never happens
   return x;
}

int main() {
   printf("%d", sizeof(func(3))); // all that matters to sizeof is the 
                                  // return type of the function.
   return 0;
}

Resultado:

2

como shortocupa 2 bytes na minha máquina.

Alterando o tipo de retorno da função para double:

double func(short x) {
// rest all same

dará 8como saída.

codaddict
fonte
12
Apenas algumas vezes - é tempo de compilação, se possível.
Martin Beckett
10
-1, pois isso está em contradição com a resposta aceita (e correta) e não cita o padrão.
23711 Sam
1
Há um bom benefício na resolução em tempo de compilação do operador sizeof () ao trabalhar com strings. Se você tiver uma sequência inicializada como uma sequência entre aspas, em vez de usar strlen (), em que a matriz de caracteres que compreende a sequência deve ser varrida para o terminador nulo no tempo de execução, sizeof (sequência entre aspas) é conhecido no tempo de compilação, e, portanto, em tempo de execução. É algo pequeno, mas se você usar a seqüência de caracteres citada em um loop milhões e milhões de vezes, isso fará uma diferença significativa no desempenho.
precisa saber é o seguinte
Se você realmente o usa milhões e milhões de vezes em um loop, não seria muito mais sensato considerar o cálculo do comprimento fora do loop? Eu certamente espero que você não tenha milhões e milhões de diferentes constantes codificadas no código. : -o
Veky 29/03
47

sizeof(foo) tenta muito descobrir o tamanho de uma expressão em tempo de compilação:

6.5.3.4:

O operador sizeof gera o tamanho (em bytes) de seu operando, que pode ser uma expressão ou o nome entre parênteses de um tipo. O tamanho é determinado a partir do tipo do operando. O resultado é um número inteiro. Se o tipo do operando for um tipo de matriz de comprimento variável, o operando será avaliado; caso contrário, o operando não é avaliado e o resultado é uma constante inteira.

Em resumo: matrizes de comprimento variável, executadas em tempo de execução. (Nota: Matrizes de comprimento variável são um recurso específico - não matrizes alocadas com malloc(3).) Caso contrário, apenas o tipo da expressão é calculado e o mesmo no momento da compilação.

sarnold
fonte
33

sizeofé um operador interno em tempo de compilação e não é uma função. Isso fica muito claro nos casos em que você pode usá-lo sem os parênteses:

(sizeof x)  //this also works
hugomg
fonte
1
Mas como isso é uma resposta para a pergunta?
Sebastian Mach
5
@ phresnel: Isso é apenas para deixar claro que sizeof é "estranho" e não está sujeito às regras das funções normais. Eu editei o post de qualquer maneira para remover a possível confusão com operadores normais de tempo de execução como (+) e (-)
hugomg
O sizeofoperador não é um operador em tempo de compilação, você só precisa fornecer um VLA para descobrir isso.
paxdiablo 17/01
21

Nota

Esta resposta foi mesclada a partir de uma duplicata, o que explica a data final.

Original

Exceto para matrizes de comprimento variável, sizeof não avalia seus argumentos. Podemos ver isso a partir da seção padrão projecto C99 6.5.3.4 O operador sizeof parágrafo 2 , que diz:

O operador sizeof gera o tamanho (em bytes) de seu operando, que pode ser uma expressão ou o nome entre parênteses de um tipo. O tamanho é determinado a partir do tipo do operando. O resultado é um número inteiro. Se o tipo do operando for um tipo de matriz de comprimento variável, o operando será avaliado; caso contrário, o operando não é avaliado e o resultado é uma constante inteira.

Um comentário ( agora removido ) perguntou se algo assim seria avaliado em tempo de execução:

sizeof( char[x++]  ) ;

e, de fato, algo assim também funcionaria ( veja os dois ao vivo ):

sizeof( char[func()]  ) ;

já que ambos são matrizes de comprimento variável. Embora eu não veja muito uso prático em nenhum deles.

Observe que as matrizes de comprimento variável são abordadas no parágrafo 4 da seção C99 da seção padrão do rascunho :6.7.5.2

[...] Se o tamanho for uma expressão constante inteira e o tipo de elemento tiver um tamanho constante conhecido, o tipo de matriz não será um tipo de matriz de comprimento variável; caso contrário, o tipo de matriz é um tipo de matriz de comprimento variável.

Atualizar

Em C11, a resposta muda para o caso VLA; em certos casos, não é especificado se a expressão de tamanho é avaliada ou não. Na seção 6.7.6.2 Declaradores de matriz, que diz:

[...] Quando uma expressão de tamanho faz parte do operando de um operador sizeof e a alteração do valor da expressão de tamanho não afetaria o resultado do operador, não é especificado se a expressão de tamanho é avaliada ou não.

Por exemplo, em um caso como este ( veja ao vivo ):

sizeof( int (*)[x++] )
Shafik Yaghmour
fonte
1
O importante a lembrar aqui é que, na maioria das vezes, sizeofé efetivamente uma macro - ela não cria código, mas pré-computa o valor esperado e o deposita diretamente no código. Note-se que esta foi a única comportamento até C99, como VBAs não existia (i nunca tinha realmente ouvido falar deles até esta resposta, acredite ou não!)
Corley Brigman
De que maneira sizeof (char[x++]);o valor de usaria xalgo diferente de determinar o valor da expressão x++e o novo valor de x, os quais são normais com esse operador?
Supercat 24/02
@alk ... err, sim, eu quis dizer 'VLA' é claro :) Shafik - por que os avaliaria em tempo de execução? como eu disse, nunca vi VLAs, mas os tipos são conhecidos no momento da compilação, não são?
Corley Brigman
@CorleyBrigman, a resposta rápida seria b / c, o padrão diz isso, mas o motivo é b / c: não sabemos o tamanho da matriz no tempo de compilação, por isso precisamos avaliar a expressão em tempo de execução. Os VLAs são um tópico interessante, aqui estão dois posts que tenho sobre eles aqui e aqui .
Shafik Yaghmour
@ Shafik - ahh, ok, acho que minha confusão foi que eu não percebi que char[x++]é um VLA. parece efetivamente como um char*para meus olhos desconhecidos.
Corley Brigman
11

Como o operando do sizeofoperador não é avaliado, você pode fazer isso:

int f(); //no definition, which means we cannot call it

int main(void) {
        printf("%d", sizeof(f()) );  //no linker error
        return 0;
}

Demonstração on-line: http://ideone.com/S8e2Y

Ou seja, você não precisa definir a função fse ela for usada sizeofapenas. Essa técnica é usada principalmente na metaprogramação de modelos C ++, pois mesmo em C ++, o operando de sizeofnão é avaliado.

Por que isso funciona? Funciona porque o sizeofoperador não opera com valor , mas com o tipo de expressão. Portanto, quando você escreve sizeof(f()), ele opera com o tipo de expressão f()e que nada mais é do que o tipo de retorno da função f. O tipo de retorno é sempre o mesmo, independentemente do valor que a função retornaria se realmente fosse executada.

No C ++, você pode até isso:

struct A
{
  A(); //no definition, which means we cannot create instance!
  int f(); //no definition, which means we cannot call it
};

int main() {
        std::cout << sizeof(A().f())<< std::endl;
        return 0;
}

No entanto, parece que, sizeofprimeiro, eu estou criando uma instância de A, escrevendo A()e depois chamando a função fna instância, escrevendo A().f(), mas isso não acontece.

Demonstração: http://ideone.com/egPMi

Aqui está outro tópico que explica algumas outras propriedades interessantes de sizeof:

Nawaz
fonte
10

A execução não pode acontecer durante a compilação. Então ++i/ i++não vai acontecer. Também sizeof(foo())não executará a função, mas retornará o tipo correto.

rakesh
fonte
2
" A execução não pode acontecer durante a compilação. " O que você quer dizer?
23811
1
A compilação criará apenas código de objeto ... O código de objeto será executado somente quando o usuário executar o binário. Como sizeof acontece no momento da compilação, assumindo que o i ++ aumentará está errado.
rakesh
" Como sizeof acontece no tempo de compilação ", você quer dizer: "como sizeofé uma expressão constante do tempo de compilação"?
23811
Como "#define" acontece durante o pré-processamento, da mesma forma sizeof acontecerá em tempo de compilação. Durante a compilação, todas as informações de tipo estão disponíveis, então sizeof é avaliado então e durante a compilação e o valor é substituído. Como já mencionado por @pmg "Do padrão C99" antes.
rakesh
1
" Sizeof vai acontecer em tempo de compilação " por algo que não é uma matriz de comprimento variável
curiousguy
0

sizeofé executado em tempo de compilação, mas x++só pode ser avaliado em tempo de execução. Para resolver isso, o padrão C ++ determina que o operando de sizeofnão seja avaliado. O Padrão C diz:

Se o tipo do operando [of sizeof] for um tipo de matriz de comprimento variável, o operando será avaliado; caso contrário, o operando não é avaliado e o resultado é uma constante inteira.

Edward Karak
fonte
Não há VLA em C ++.
LF
-2

sizeof() O operador fornece apenas o tamanho do tipo de dados; ele não avalia os elementos internos.

munna
fonte
Isso é falso, o sizeof()operador age recursivamente e obterá o tamanho em bytes de todos os elementos de um contêiner, membros de uma classe ou estrutura etc. Você pode provar isso facilmente, criando uma classe simples com alguns membros e chamando sizeof()por isso. (No entanto, qualquer coisa lá dentro, que seja um ponteiro, não pode ver o tamanho de - apenas o tamanho do ponteiro.) Isso acontece em tempo de compilação, como outros comentadores declararam: as expressões dentro sizeof()do não são avaliadas.
Tyler Shellberg 03/03