¿Por qué sizeof (x ++) no incrementa x?

505

Aquí está el código compilado en dev c ++ windows:

#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 después de ejecutar la nota 1 . Sin embargo, la salida es:

4 and 5

¿Alguien puede explicar por qué xno se incrementa después de la nota 1 ?

Neigyl R. Noval
fuente
37
Notaría que DevC ++ usa un compilador obsoleto muy antiguo, es posible que desee actualizar a un IDE más nuevo, por ejemplo, Codeblocks Eclipse o Visual Studio
Tom J Nowell
44
++ x produce el mismo resultado que x ++, cygwin y gcc 3.4.4.
yehnan
66
@Tim Pletzcker Porque el primer valor es el tamaño de la variable, y no la variable en sí.
Surfbutler
Mi respuesta se agregó debido a una fusión con esta, mi respuesta realmente muestra cómo puede obtener una evaluación en tiempo de ejecución en el caso de VLAsque ninguno de los otros lo haga.
Shafik Yaghmour
2
Se debe evitar este tipo de escritura ya que puede causar efectos secundarios indeseables. Tu pregunta ya es una de ellas.
user666412

Respuestas:

537

Del estándar C99 (el énfasis es mío)

6.5.3.4/2

El operador sizeof produce el tamaño (en bytes) de su operando, que puede ser una expresión o el nombre entre paréntesis de un tipo. El tamaño se determina a partir del tipo de operando. El resultado es un entero. Si el tipo del operando es un tipo de matriz de longitud variable, se evalúa el operando; de lo contrario, el operando no se evalúa y el resultado es una constante entera.

pmg
fuente
51
"Si el tipo del operando es un tipo de matriz de longitud variable, el operando se evalúa" ¡guau! Nunca me di cuenta de eso
Kos
66
¿Qué quieres decir con tipo de matriz de longitud variable? ¿Eso significa que el operando es una matriz? El código en este caso no es una matriz. ¿Puedes aclararme las cosas?
Neigyl R. Noval
37
Una matriz de longitud variable es una matriz declarada con un tamaño desconocido durante la compilación, por ejemplo, si lee Ndesde stdin y make int array[N]. Esta es una de las características de C99, no disponible en C ++.
Kos
21
@LegendofCage, en particular, esto significaría que en algo como sizeof(int[++x])(realmente, realmente una mala idea, de todos modos) se ++podría evaluar.
Jens Gustedt
3
@ Joe Wreschnig: Se evaluó gcc, clangy al ideone.com/Pf7iF
JFS
190

sizeofes un operador en tiempo de compilación, por lo que en el momento de la compilación sizeofy su operando se reemplaza por el valor del resultado. El operando no se evalúa (excepto cuando es una matriz de longitud variable); solo importa el tipo de resultado.

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;
}

Salida:

2

como shortocupa 2 bytes en mi máquina.

Cambiar el tipo de retorno de la función a double:

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

dará 8como salida.

codictorio
fuente
12
Solo a veces, es tiempo de compilación si es posible.
Martin Beckett
10
-1, ya que esto está en contradicción con la respuesta aceptada (y correcta), y no cita el estándar.
sam hocevar
1
Hay una buena ventaja para la resolución en tiempo de compilación del operador sizeof () al trabajar con cadenas. Si tiene una cadena que se inicializa como una cadena entre comillas, en lugar de usar strlen (), donde la matriz de caracteres que comprende la cadena debe escanearse para encontrar el terminador nulo en tiempo de ejecución, se conoce sizeof (quoted_string) en tiempo de compilación, y por lo tanto en tiempo de ejecución. Es algo pequeño, pero si usa la cadena citada en un bucle millones y millones de veces, hace una diferencia significativa en el rendimiento.
user2548100
Si realmente lo usa millones y millones de veces en un ciclo, ¿no sería mucho más sensato factorizar el cálculo de longitud fuera del ciclo? Seguramente espero que no tenga millones y millones de constantes codificadas diferentes en su código. : -o
Veky
47

sizeof(foo) intenta realmente descubrir el tamaño de una expresión en tiempo de compilación:

6.5.3.4:

El operador sizeof produce el tamaño (en bytes) de su operando, que puede ser una expresión o el nombre entre paréntesis de un tipo. El tamaño se determina a partir del tipo de operando. El resultado es un entero. Si el tipo del operando es un tipo de matriz de longitud variable, se evalúa el operando; de lo contrario, el operando no se evalúa y el resultado es una constante entera.

En resumen: matrices de longitud variable, ejecutar en tiempo de ejecución. (Nota: Las matrices de longitud variable son una característica específica, no matrices asignadas con malloc(3)). De lo contrario, solo se calcula el tipo de expresión, y eso en tiempo de compilación.

sarnold
fuente
33

sizeofes un operador integrado en tiempo de compilación y no es una función. Esto se vuelve muy claro en los casos en que puede usarlo sin el paréntesis:

(sizeof x)  //this also works
hugomg
fuente
1
Pero, ¿cómo es esto una respuesta a la pregunta?
Sebastian Mach
55
@phresnel: Esto es solo para dejar en claro que sizeof es "extraño" y no está sujeto a las reglas de las funciones normales. Edité la publicación de todos modos para eliminar la posible confusión con los operadores de tiempo de ejecución normales como (+) y (-)
hugomg
El sizeofoperador no es un operador de tiempo de compilación, solo tiene que darle un VLA para resolver esto.
paxdiablo
21

Nota

Esta respuesta se fusionó a partir de un duplicado, lo que explica la fecha de retraso.

Original

A excepción de las matrices de longitud variable sizeof no evalúa sus argumentos. Podemos ver esto en el borrador de la sección estándar C99 6.5.3.4 El tamaño del párrafo 2 del operador que dice:

El operador sizeof produce el tamaño (en bytes) de su operando, que puede ser una expresión o el nombre entre paréntesis de un tipo. El tamaño se determina a partir del tipo de operando. El resultado es un entero. Si el tipo del operando es un tipo de matriz de longitud variable, se evalúa el operando; de lo contrario, el operando no se evalúa y el resultado es una constante entera.

Un comentario ( ahora eliminado ) preguntaba si algo como esto se evaluaría en tiempo de ejecución:

sizeof( char[x++]  ) ;

y de hecho lo haría, algo como esto también funcionaría ( verlos a ambos en vivo ):

sizeof( char[func()]  ) ;

ya que ambos son matrices de longitud variable. Aunque, no veo mucho uso práctico en ninguno de los dos.

Nota, las matrices de longitud variable están cubiertos en el proyecto de norma C99 sección 6.7.5.2 de matriz declaradores párrafo 4 :

[...] Si el tamaño es una expresión constante entera y el tipo de elemento tiene un tamaño constante conocido, el tipo de matriz no es un tipo de matriz de longitud variable; de lo contrario, el tipo de matriz es un tipo de matriz de longitud variable.

Actualizar

En C11, la respuesta cambia para el caso de VLA, en ciertos casos no se especifica si la expresión de tamaño se evalúa o no. De la sección 6.7.6.2 Declaradores de matriz que dice:

[...] Cuando una expresión de tamaño es parte del operando de un operador de sizeof y cambiar el valor de la expresión de tamaño no afectaría el resultado del operador, no se especifica si se evalúa o no la expresión de tamaño.

Por ejemplo, en un caso como este ( verlo en vivo ):

sizeof( int (*)[x++] )
Shafik Yaghmour
fuente
1
Lo importante a recordar aquí es que la mayoría de las veces sizeofes efectivamente una macro: no crea código, sino que calcula previamente el valor esperado y lo deposita directamente en el código. Tenga en cuenta que este fue el único comportamiento hasta C99, ya que los VBA no existían (nunca había oído hablar de ellos hasta esta respuesta, ¡créanlo o no!)
Corley Brigman
¿De qué manera sizeof (char[x++]);usaría el valor de xpara otra cosa que no sea determinar el valor de la expresión x++y el nuevo valor para x, los cuales son normales con ese operador?
supercat
@alk ... err, sí, me refería a 'VLA' por supuesto :) Shafik: ¿por qué evaluarían en tiempo de ejecución? como dije, nunca he visto VLA, pero los tipos de esos son conocidos en el momento de la compilación, ¿no?
Corley Brigman
@CorleyBrigman la respuesta rápida sería b / c, el estándar lo dice, pero la razón es b / c, no sabemos el tamaño de la matriz en el momento de la compilación, por lo que tenemos que evaluar la expresión en tiempo de ejecución. Los VLA son un tema interesante, aquí hay dos publicaciones que tengo sobre ellos aquí y aquí .
Shafik Yaghmour
@ Shafik - ahh, ok, supongo que mi confusión fue que no me di cuenta que char[x++]es un VLA. se ve efectivamente como un char*para mis ojos desconocidos.
Corley Brigman
11

Como el operando del sizeofoperador no se evalúa, puede hacer esto:

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

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

Demostración en línea: http://ideone.com/S8e2Y

Es decir, no necesita definir la función fsi solo se usa sizeof. Esta técnica se usa principalmente en la metaprogramación de plantillas de C ++, ya que incluso en C ++, el operando de sizeofno se evalúa.

¿Por qué funciona esto? Funciona porque el sizeofoperador no opera según el valor , sino que funciona según el tipo de expresión. Entonces, cuando escribe sizeof(f()), opera sobre el tipo de expresión f(), y que no es más que el tipo de retorno de la función f. El tipo de retorno es siempre el mismo, sin importar qué valor devolvería la función si realmente se ejecuta.

En C ++, puedes incluso esto:

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;
}

Sin embargo, parece que, en primer lugar sizeof, estoy creando una instancia de A, escribiendo A(), y luego llamando a la función fen la instancia, escribiendo A().f(), pero no sucede tal cosa.

Demostración: http://ideone.com/egPMi

Aquí hay otro tema que explica algunas otras propiedades interesantes de sizeof:

Nawaz
fuente
10

La ejecución no puede ocurrir durante la compilación. Entonces ++i/ i++no sucederá. Tampoco sizeof(foo())ejecutará la función pero devolverá el tipo correcto.

Rakesh
fuente
2
" La ejecución no puede ocurrir durante la compilación ", ¿qué quieres decir?
curioso
1
La compilación solo creará código objeto ... El código objeto se ejecutará solo cuando el usuario ejecute el binario. Como sizeof sucede en el momento de la compilación, suponiendo que i ++ se incremente es incorrecto.
Rakesh
" Como sizeof ocurre en tiempo de compilación " quiere decir: "como sizeofes una expresión constante de tiempo de compilación"
curioso
Al igual que "#define" ocurre durante el preprocesamiento, de forma similar sizeof ocurrirá en tiempo de compilación. Durante la compilación, toda la información de tipo está disponible, por lo que se evalúa sizeof en ese momento y durante la compilación, se reemplaza el valor. Como ya mencionó @pmg "Del estándar C99" antes.
Rakesh
1
" Sizeof ocurrirá en tiempo de compilación " por algo que no es una matriz de longitud variable
curiousguy
0

sizeofse ejecuta en tiempo de compilación, pero x++solo se puede evaluar en tiempo de ejecución. Para resolver esto, el estándar C ++ dicta que el operando de sizeofno se evalúa. El estándar C dice:

Si el tipo del operando [de sizeof] es un tipo de matriz de longitud variable, se evalúa el operando; de lo contrario, el operando no se evalúa y el resultado es una constante entera.

Edward Karak
fuente
No hay VLA en C ++.
LF
-2

sizeof() El operador solo da el tamaño del tipo de datos, no evalúa los elementos internos.

munna
fuente
Esto es falso, el sizeof()operador actúa de forma recursiva y obtendrá el tamaño en bytes de todos los elementos de un contenedor, miembros de una clase o estructura, etc. Puede probarlo usted mismo muy fácilmente creando una clase simple con unos pocos miembros y llamando sizeof()en él. (Sin embargo, cualquier cosa allí que sea un puntero no puede ver el tamaño, solo el tamaño del puntero). Esto sucede todo en el momento de la compilación, como dijeron otros comentaristas: las expresiones dentro del sizeof()no se evalúan.
Tyler Shellberg