¿Cuál es el tamaño del vacío?

77

¿Qué produciría esta declaración?

void *p = malloc(sizeof(void));

Editar: una extensión de la pregunta.

Si sizeof (void) produce 1 en el compilador GCC, entonces se asigna 1 byte de memoria y el puntero p apunta a ese byte y ¿se incrementaría p ++ a 0x2346? Suponga que p fuera 0x2345. Estoy hablando de p y no de * p.

Lakshmi
fuente
4
FYI. Esto es C. no estándar. En muchos compiladores, esto sería un error en tiempo de compilación.
Johan Kotlinski
Por qué necesitas saberlo. Comprender esto puede ayudarnos a responder la pregunta.
Martin York
4
Comentando su edición: sí, en GCC (solo), incrementar un puntero vacío agrega uno al valor. Si valora la portabilidad de su código, no abuse de la libertad que le brinda GCC. Es completamente no estándar. Y GCC lo admite con '-std = c99 -pedantic'.
Jonathan Leffler
1
Propuesta nula
alfC

Respuestas:

56

El tipo voidno tiene tamaño; eso sería un error de compilación. Por la misma razón, no puede hacer algo como:

void n;

EDITAR. Para mi sorpresa, hacer sizeof(void)realmente se compila en GNU C:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc -w - && ./a.out 
1

Sin embargo, en C ++ no:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc++ -w - && ./a.out 
<stdin>: In function 'int main()':
<stdin>:1: error: invalid application of 'sizeof' to a void type
<stdin>:1: error: 'printf' was not declared in this scope
reko_t
fuente
31
Pero GCC tiene una opción para tratar 'sizeof (void) == sizeof (char)' ... Ver -Wpointer-arith
Jonathan Leffler
5
@Jon esto es para tontos de principios de los 80 que recién estaban comenzando a aprender a programar. Los estándares no se decidieron ampliamente.
unixman83
5
sizeof (void)es una violación de la restricción. Un compilador de C conforme (que gcc no es por defecto) debe al menos advertirlo y puede (y en mi humilde opinión debería) rechazarlo. Referencia: N1570 6.5.3.4p1 ( voides un tipo incompleto). Ha sido así desde el estándar de 1989, que se introdujo void.
Keith Thompson
3
@ unixman83: Eso no tiene sentido. voidno existía antes de que fuera introducido por el estándar ANSI C de 1989, el mismo estándar que sizeof (void)violaba las restricciones.
Keith Thompson
1
Sizeof void se compila porque lo necesita para trabajar con punteros void *.
Kelin
41

Si está usando GCC y no está usando indicadores de compilación que eliminen las extensiones específicas del compilador, entonces sizeof(void)es 1. GCC tiene una extensión no estándar que hace eso.

En general, voides un tipo incompleto y no puede usar sizeof para tipos incompletos.

hrnt
fuente
2
La opción -Wpointer-arithestá implícita en -pedantic. Siempre usé -pedantic. :)
Josh Lee
1
@jleedev De hecho, también uso -pedantic tanto como puedo. Sin embargo, desafortunadamente, algunos encabezados de bibliotecas de terceros hacen que gcc -pedantic sea muy triste :(
hrnt
1
+1 Realmente, ¿por qué GCC tiene miles de extensiones inútiles?
@ user142019: ¿porque son útiles? Y ayuda a obtener un modelo de backend consistente a través de diferentes lenguajes de programación y arquitectura de hardware.
kriss
16

Aunque voidpuede estar en el lugar de un tipo, en realidad no puede tener un valor. Por tanto, no tiene tamaño en la memoria. Obtener el tamaño de un voidno está definido.

Un void puntero es simplemente una construcción del lenguaje que significa un puntero a la memoria sin tipo .

Konrad Rudolph
fuente
1
Y qué ? Cualquier estructura vacía no debería consumir tamaño en la memoria y eso debería ser un problema. Bueno, en realidad me pregunto, si las estructuras vacías realmente usan 0 bytes en C ++ (como es cierto en C) parece que sizeof devuelve 1 para ellos.
kriss
1
@kriss No estoy seguro de lo que estás diciendo, pero como usted ha notado usted mismo, estructuras vacías hacer ocupo de memoria por razones de identidad de los objetos.
Konrad Rudolph
1
de hecho, pero no era cierto en C y usar la memoria para objetos vacíos se siente mal.
kriss
13

voidno tiene tamaño. Tanto en C como en C ++, la expresión sizeof (void)no es válida.

En C, citando N1570 6.5.3.4 párrafo 1:

El sizeofoperador no se aplicará a una expresión que tenga un tipo de función o un tipo incompleto , al nombre entre paréntesis de dicho tipo, ni a una expresión que designe un miembro de campo de bits.

(N1570 es un borrador de la norma ISO C de 2011).

voides un tipo incompleto. Este párrafo es una restricción , lo que significa que cualquier compilador de C conforme debe diagnosticar cualquier violación del mismo. (El mensaje de diagnóstico puede ser una advertencia no fatal).

El estándar C ++ 11 tiene una redacción muy similar. Ambas ediciones se publicaron después de que se hizo esta pregunta, pero las reglas se remontan al estándar ANSI C de 1989 y los primeros estándares C ++. De hecho, la regla que voides de un tipo incompleto a la que sizeofno se puede aplicar se remonta exactamente a la introducción de voiden el idioma.

gcc tiene una extensión que se trata sizeof (void)como 1. gcc no es un compilador de C conforme de forma predeterminada, por lo que en su modo predeterminado no advierte sizeof (void). Se permiten extensiones como esta incluso para compiladores de C totalmente conformes, pero el diagnóstico aún es necesario.

Keith Thompson
fuente
Supongo que permitieron la sizeof(void) == 1coherencia con la aritmética del voidpuntero en el puntero, que también es una extensión gcc . Citando de su documentación: A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.. Pero aún así, el mensaje de diagnóstico debería estar presente de forma predeterminada para C, ya que estándar lo requiere.
Grzegorz Szpetkowski
2
@GrzegorzSzpetkowski: (Respondiendo un par de años después). Sí, el estándar requiere un diagnóstico, pero gcc no es un compilador C conforme por defecto. Lógicamente, el estándar C no impone requisitos a las implementaciones que no pretenden ajustarse a él. Se puede hacer que gcc se ajuste (casi) a las opciones correctas de la línea de comandos, como -std=c11 -pedantic. Con tales opciones, emite el diagnóstico requerido.
Keith Thompson
6

sizeof()no se puede aplicar a tipos incompletos. Y voides un tipo incompleto que no se puede completar.

Aleksei Potov
fuente
3

C ª, sizeof(void) == 1 en GCC, pero esto parece depender de su compilador.

En C ++, obtengo:

En la función 'int main ()':
Línea 2: error: aplicación no válida de 'sizeof' a un tipo vacío
la compilación terminó debido a -Wfatal-errors.
Greg Hewgill
fuente
3
Solo en GCC es sizeof (void) == 1; en el estándar, es un comportamiento indefinido.
Jonathan Leffler
Actualizado para agregar una nota específica de GCC.
Greg Hewgill
2
@JonathanLeffler: No es simplemente un comportamiento indefinido. Es una violación de la restricción. N1570 6.5.3.4p1.
Keith Thompson
No es solo GCC, es GCC y cualquier otro compilador que esté diseñado para ser compatible con él (incluidos clang, icc, tcc y probablemente otros).
Keith Thompson
0

A la segunda parte de la pregunta: Note que sizeof (void *)! = Sizeof (void). En un arco de 32 bits, el tamaño de (void *) es de 4 bytes, por lo que p ++ se establecería en consecuencia. La cantidad en la que se incrementa un puntero depende de los datos a los que apunta. Entonces, se incrementará en 1 byte.


fuente
1
entonces p estaba apuntando a datos de 1 byte ya que sizeof (void) da 1 entonces p ++ significa que p sería un incremento en 1 y no 4 ??
Lakshmi
1
La cantidad en la que se incrementa un puntero depende de los datos a los que apunta. Entonces sí.
p es de tipo void * (puntero void), no void. Por lo tanto, se incrementaría con el tamaño del tipo void * (que suele ser 4 en sistemas de 32 bits).
Technowise
4
@Technowise: no. Cualquier puntero de tipo T*se incrementa en sizeof(T)(y no sizeof(T*) , que casi siempre sería lo mismo, excepto quizás para punteros de función) cuando se incrementa. La excepción es, por supuesto void(y nuevamente la excepción es cuando se tienen habilitadas las extensiones GCC).
Konrad Rudolph
1
@Amit: ¿Cómo llegó a la conclusión de que el tamaño de los datos a los que void*apunta un puntero es de 1 byte? sizeof (void)es una violación de la restricción. (Una extensión de gcc lo trata como 1.)
Keith Thompson
-1

mientras que sizeof (void) quizás no tenga sentido en sí mismo, es importante cuando estás haciendo cualquier cálculo de puntero.

p.ej.

 void *p;
 while(...)
      p++;

Si sizeof (void) se considera 1, esto funcionará. Si sizeof (void) se considera 0, entonces golpea un bucle infinito.

usuario906752
fuente
4
El fragmento de código de ejemplo es ilegal en C y C ++. Algunos compiladores lo permiten de todos modos.
James Roth
1
No, no es importante en absoluto (aunque los autores de gcc aparentemente pensaron que sí). Si desea hacer aritmética de punteros en punteros de bytes, use unsigned char*.
Keith Thompson
@KeithThompson: incluso charno se garantiza que sea de 1 byte. He trabajado en un sistema que lo puso en 32 bits.
Ash
@ash: Sí, charse garantiza que sea de 1 byte. Si ha trabajado en un sistema con 32 bits char, entonces un byte en ese sistema es de 32 bits ( CHAR_BIT == 32). Así es como el estándar C define "byte". C estándar, 6.5.3.4 párrafos 2 y 4: "El sizeofoperador da el tamaño (en bytes) de su operando [...] Cuando. sizeofSe aplica a un operando que tiene tipo char, unsigned charo signed char, (o una versión calificada del mismo) el resultado es 1. "
Keith Thompson
¿Cuándo salió el estándar C 6.5.3.4? Trabajé en este sistema a mediados de los 90.
Ash
-1

La mayoría de los compiladores de C ++ optaron por generar un error de compilación al intentar obtener sizeof(void).

Al compilar C, gcc no se ajusta y eligió definirlo sizeof(void)como 1. Puede parecer extraño, pero tiene una razón fundamental. Cuando hace aritmética de punteros, agregar o quitar una unidad significa agregar o quitar el objeto apuntado al tamaño. Por lo tanto, definir sizeof(void)como 1 ayuda a definir void*como un puntero a un byte (dirección de memoria sin escribir). De lo contrario, tendrías comportamientos sorprendentes usando aritmética de punteros como p+1 == p whenp void*. Esta aritmética de punteros en punteros vacíos no está permitida en c ++ pero funciona bien cuando se compila C con gcc.

La forma estándar recomendada sería utilizar char*para ese tipo de propósito (puntero a byte).

Otra diferencia similar entre C y C ++ cuando se usa sizeof ocurre cuando define una estructura vacía como:

struct Empty {
} empty;

Usar gcc como mi compilador de C sizeof(empty)devuelve 0. Usar g ++ el mismo código devolverá 1.

No estoy seguro de qué establece los estándares C y C ++ en este punto, pero creo que definir el tamaño de algunas estructuras / objetos vacíos ayuda con la administración de referencias para evitar que dos referencias a objetos consecutivos diferentes, la primera vacía, obtengan el misma direccion. Si las referencias se implementan utilizando punteros ocultos como se hace a menudo, asegurarse de que haya direcciones diferentes ayudará a compararlos.

Pero esto es simplemente evitar un comportamiento sorprendente (comparación de casos de esquina de referencias) introduciendo otro (objetos vacíos, incluso los POD consumen al menos 1 byte de memoria).

kriss
fuente
6
"C eligió en su lugar definir sizeof (void) como 1." - No eso está mal. sizeof(void)no está definido en C, igual que en C ++. GCC simplemente no se ajusta a este punto, y la aritmética de voidpunteros en punteros tampoco está definida, void* p; p++;no es válida. Incluso GCC le dará una advertencia al compilar con la -pedanticbandera. Y también se equivoca sobre el comportamiento predeterminado de GCC para C ++: de forma predeterminada, esto se trata como un error .
Konrad Rudolph
@Konrad: sí, estoy bastante seguro de que no era cierto con la versión anterior de gcc, pero las actualizadas hacen lo que dices.
kriss
Al menos en C, una structdefinición vacía es un error de sintaxis; El soporte de gcc es una extensión. Pueden ser válidos en C ++; No estoy seguro.
Keith Thompson