sintaxis array c sizeof variable-length-array

c - array - sizeof(int)



¿Se evalúa el operando de `sizeof` con un VLA? (3)

De hecho, el Estándar parece implicar que el comportamiento no está definido:

re-cotizando N1570 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.

Creo que la redacción de la Norma es confusa: el operando que se evalúa no significa que se evaluará *bar . Evaluar *bar no ayuda de ninguna manera a calcular su tamaño. sizeof(*bar) no necesita ser computado en tiempo de ejecución, pero el código generado para esto no necesita desreferenciar la bar , es más probable que recupere la información de tamaño de una variable oculta que contiene el resultado del cálculo de tamaño en el momento de La instanciación del bar .

Un argumento en la sección de comentarios de esta respuesta me impulsó a hacer esta pregunta.

En el siguiente código, la bar apunta a una matriz de longitud variable, por lo que el sizeof se determina en tiempo de ejecución en lugar de compilar.

int foo = 100; double (*bar)[foo];

El argumento era si el uso de sizeof evalúa su operando cuando el operando es una matriz de longitud variable, lo que hace que el comportamiento sizeof(*bar) no esté definido cuando la bar no se inicializa.

¿Es un comportamiento indefinido usar sizeof(*bar) porque estoy desreferenciado un puntero no inicializado? ¿Se evalúa realmente el operando de sizeof cuando el tipo es una matriz de longitud variable, o simplemente determina su tipo (cómo suele funcionar sizeof )?

Edit: Todo el mundo parece estar citando este pasaje del borrador C11. ¿Alguien sabe si esta es la redacción de la norma oficial?


Otras dos respuestas ya han citado N1570 6.5.3.4p2:

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.

Según ese párrafo de la norma, sí, se evalúa el operando de sizeof .

Voy a argumentar que esto es un defecto en la norma; algo se evalúa en tiempo de ejecución, pero el operando no lo es.

Consideremos un ejemplo más simple:

int len = 100; double vla[len]; printf("sizeof vla = %zu/n", sizeof vla);

De acuerdo con el estándar, sizeof vla evalúa la expresión vla . Pero ¿qué significa eso?

En la mayoría de los contextos, la evaluación de una expresión de matriz produce la dirección del elemento inicial, pero el operador sizeof es una excepción explícita a eso. Podríamos asumir que evaluar vla significa acceder a los valores de sus elementos, que tiene un comportamiento indefinido ya que esos elementos no se han inicializado. Pero no hay otro contexto en el que la evaluación de una expresión de matriz acceda a los valores de sus elementos, y no hay necesidad de hacerlo en este caso. (Corrección: si se usa un literal de cadena para inicializar un objeto de matriz, se evalúan los valores de los elementos).

Cuando se ejecuta la declaración de vla , el compilador creará algunos metadatos anónimos para mantener la longitud de la matriz (debe hacerlo, ya que al asignar un nuevo valor a len después de definir y asignar vla la longitud de vla no se modifica). Todo lo que se debe hacer para determinar el sizeof vla es multiplicar ese valor almacenado por sizeof (double) (o simplemente recuperar el valor almacenado si almacena el tamaño en bytes).

sizeof también se puede aplicar a un nombre de tipo entre paréntesis:

int len = 100; printf("sizeof (double[len]) = %zu/n", sizeof (double[len]));

De acuerdo con el estándar, la expresión sizeof evalúa el tipo . Qué significa eso? Claramente tiene que evaluar el valor actual de len . Otro ejemplo:

size_t func(void); printf("sizeof (double[func()]) = %zu/n", sizeof (double[func()]));

Aquí el nombre del tipo incluye una llamada a la función. La evaluación de la expresión sizeof debe llamar a la función.

Pero en todos estos casos, no hay necesidad real de evaluar los elementos del objeto de la matriz (si hay alguno), y no tiene sentido hacerlo.

sizeof aplicado a cualquier otra cosa que no sea un VLA puede evaluarse en tiempo de compilación. La diferencia cuando se aplica sizeof a un VLA (ya sea un objeto o un tipo) es que algo debe evaluarse en tiempo de ejecución. Pero lo que hay que evaluar no es el operando de sizeof ; es lo que se necesita para determinar el tamaño del operando, que nunca es el operando en sí mismo.

El estándar dice que el operando de sizeof se evalúa si ese operando es de tipo de matriz de longitud variable. Eso es un defecto en la norma.

Volviendo al ejemplo en la pregunta:

int foo = 100; double (*bar)[foo] = NULL; printf("sizeof *bar = %zu/n", sizeof *bar);

He agregado una inicialización a NULL para que sea aún más claro que la bar desreferenciación tiene un comportamiento indefinido.

*bar es de tipo int[foo] , que es un tipo VLA. En principio, se evalúa *bar , lo que tendría un comportamiento indefinido ya que la bar no está inicializada. Pero una vez más, no hay necesidad de desreferenciar la bar . El compilador generará algo de código cuando procese el tipo int[foo] , incluido guardar el valor de foo (o foo * sizeof (int) ) en una variable anónima. Todo lo que tiene que hacer para evaluar sizeof *bar es recuperar el valor de esa variable anónima. Y si la norma se actualizara para definir la semántica de sizeof consistente , sería claro que la evaluación de sizeof *bar está bien definida y produce 100 * sizeof (double) sin tener que desreferir la bar referencia.


Sí, esto causa un comportamiento indefinido.

En N1570 6.5.3.4/2 tenemos:

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.

Ahora tenemos la pregunta: ¿es el tipo de *bar un tipo de matriz de longitud variable?

Dado que la bar se declara como puntero a VLA, la anulación de la referencia debe producir un VLA. (Pero no veo un texto concreto que especifique si lo hace o no).

Nota: Se podría tener más discusión aquí, quizás se podría argumentar que *bar tiene el tipo double[100] que no es un VLA .

Suponiendo que estemos de acuerdo en que el tipo de *bar es en realidad un tipo de VLA, entonces en la sizeof *bar se evalúa la expresión *bar .

bar es indeterminada en este punto. Ahora mirando a 6.3.2.1/1:

si un lvalue no designa un objeto cuando se evalúa, el comportamiento no está definido

Como la bar no apunta a un objeto (por ser indeterminado), la *bar evaluación *bar provoca un comportamiento indefinido.