que - punteros en c ejemplos
¿Cuál es el tipo de puntero a una matriz de longitud variable en C? (1)
Con un VLA, el operador sizeof
no es una constante de compilación. Se evalúa en tiempo de ejecución. En su pregunta, el tipo de &arr
es int (*)[n];
- un puntero a una matriz de n
valores del tipo int
, donde n
es un valor en tiempo de ejecución. Por lo tanto, como observa, &arr + 1
(los paréntesis no son necesarios, excepto alrededor de los comentarios entre paréntesis que indican que los paréntesis no son necesarios) es el comienzo de la matriz después de arr
: la dirección es bytes sizeof(arr)
más allá del valor de arr
.
Usted podría imprimir el tamaño de arr
; le daría el tamaño apropiado ( printf()
modifier z
). Podría imprimir la diferencia entre &arr + 1
y arr
y obtendría el tamaño como un ptrdiff_t
( printf()
t
).
Por lo tanto:
#include <stdio.h>
int main(void)
{
int n;
if (scanf("%d", &n) == 1)
{
int arr[n];
void* ptr = (&arr) + 1;
printf("%p/n", arr);
printf("%p/n", ptr);
printf("Size: %zu/n", sizeof(arr));
printf("Diff: %td/n", ptr - (void *)arr);
}
return 0;
}
Y dos ejecuciones de muestra (nombre del programa vla59
):
$ vla59
23
0x7ffee8106410
0x7ffee810646c
Size: 92
Diff: 92
$ vla59
16
0x7ffeeace7420
0x7ffeeace7460
Size: 64
Diff: 64
$
Sin recompilación, pero sizeof()
es correcto cada vez que se ejecuta el programa. Se calcula en tiempo de ejecución.
De hecho, incluso puedes usar un bucle con un tamaño diferente cada vez:
#include <stdio.h>
int main(void)
{
int n;
while (printf("Size: ") > 0 && scanf("%d", &n) == 1 && n > 0)
{
int arr[n];
void* ptr = (&arr) + 1;
printf("Base: %p/n", arr);
printf("Next: %p/n", ptr);
printf("Size: %zu/n", sizeof(arr));
printf("Diff: %td/n", ptr - (void *)arr);
}
return 0;
}
vla11
de la muestra de vla11
:
$ vla11
Size: 23
Base: 0x7ffee3e55410
Next: 0x7ffee3e5546c
Size: 92
Diff: 92
Size: 16
Base: 0x7ffee3e55420
Next: 0x7ffee3e55460
Size: 64
Diff: 64
Size: 2234
Base: 0x7ffee3e53180
Next: 0x7ffee3e55468
Size: 8936
Diff: 8936
Size: -1
$
Aquí hay un programa corto de C que solicita al usuario un número, crea una matriz de longitud variable de ese tamaño y luego utiliza la aritmética de punteros para pasar por encima de los elementos asignados:
#include <stdio.h>
int main() {
/* Read a size from the user; inhibits compiler optimizations. */
int n;
scanf("%d", &n); // Yes, I should error-check. :-)
/* We now have a VLA. */
int arr[n];
/* What is the type of &arr? */
void* ptr = (&arr) + 1;
/* Seems like this skipped over things properly... */
printf("%p/n", arr);
printf("%p/n", ptr);
}
Puedes probar esto en ideone si quieres. La salida sugiere que la línea
void* ptr = (&arr) + 1;
toma la dirección de arr
y, de forma consciente del tamaño, recorre todos los n
elementos de la matriz de longitud variable.
Si esto no fuera una matriz de longitud variable, me sentiría completamente cómodo con cómo funciona esto. El compilador sabría el tipo de arr
(sería int (*) [K]
para alguna constante K
), así que cuando agregamos uno a &arr
, podría saltar el número correcto de bytes.
Está claro cómo, en el tiempo de ejecución, podríamos evaluar (&arr) + 1
. El compilador oculta el tamaño de arr
algún lugar de la pila, y cuando agregamos uno a (&arr)
, sabe cargar ese tamaño para calcular cuántos bytes omitir.
Sin embargo, lo que no sé es qué es el lenguaje que dice el tipo de expresión &arr
. ¿Se le asigna algún tipo estático que indica que es una matriz de longitud variable (algo como int (*) [??]
)? ¿Dice la especificación "el tipo de expresión es int (*) [K]
, donde K
es del tamaño que se asigna a la matriz en tiempo de ejecución?" ¿La especificación no permite tomar la dirección de una matriz de longitud variable, y el compilador simplemente pasa a permitirlo?