number - ¿Puede "sizeof(arr[0])" llevar a un comportamiento indefinido?
size of array php (5)
Como una expresión (no una declaración), ptr[0]
es idéntico a *ptr
.
Mientras se ptr
el tipo de ptr
y no apunte a vacío, se garantiza que sizeof(*ptr)
funcionará tanto en sizeof(*ptr)
como en sizeof(ptr[0])
.
Hay un patrón bien conocido de averiguar la longitud del arreglo:
int arr[10];
size_t len = sizeof(arr) / sizeof(arr[0]);
assert(len == 10);
Este patrón se aplica a matrices estáticas y matrices automáticas de tamaño constante. También se aplica a matrices de longitud variable en C99.
Quiero aplicar una idea similar para averiguar el tamaño de la matriz dinámica en bytes:
size_t known_len = 10;
int *ptr = malloc(known_len * sizeof(int));
size_t size = known_len * sizeof(ptr[0]);
assert(size == known_len * sizeof(int));
Esto es mejor que known_len * sizeof(int)
porque sizeof(ptr[0])
no se refiere al tipo de elemento de matriz real. Por lo tanto, no se requiere un lector del código para saber el tipo.
Sin embargo, no me queda claro si la expresión sizeof(ptr[0])
puede llevar a un comportamiento indefinido. A medida que se expande:
sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr)
La expresión del resultado es cuestionable en caso de que ptr
sea 0
:
sizeof(*((int*) 0))
Según una norma C99:
(C99, 6.3.2.3p3): "Una expresión de constante entera con el valor
0
, o una expresión de este tipo convertida en tipovoid *
, se denomina constante de puntero nulo". La desreferenciación de un puntero nulo es un comportamiento indefinido.(C99, 6.5.3.2.p4) "Si se ha asignado un valor no válido al puntero, el comportamiento del operador unario
*
no está definido.87)"87): "Entre los valores no válidos para anular la referencia de un puntero por el operador unario
*
, se encuentran un puntero nulo, una dirección alineada de forma inadecuada para el tipo de objeto apuntado y la dirección de un objeto después del final de su vida útil".
Pero nunca se especifica si el tamaño de dicha expresión puede llevar a un comportamiento indefinido. De hecho, tal tamaño debe evaluarse en el momento de la compilación.
Mis preguntas son:
- ¿Se puede usar la expresión
sizeof(ptr[0])
en el código cuando se conoce el tipo deptr
se conoce el valor deptr
? - ¿Se puede justificar tal uso según la norma C99? GNU GCC spec?
El código es malo.
size_t known_len = 10;
int *ptr = malloc(known_len);
size_t size = known_len * sizeof(ptr[0]);
assert(size == known_len * sizeof(int));
El tamaño de la asignación de memoria desde su punto de vista es de 10 bytes. Eso no es 10 punteros a int.
known_len / sizeof(ptr[0])
le dará el número de enteros que la asignación puede contener.
Como es, es probable que salga del final de la asignación, lo que lleva a un comportamiento indefinido.
Considere malloc (known_length * sizeof ptr [0]);
En general, si me falta algo, eliminar la referencia a un puntero nulo debajo de sizeof
puede llevar a un comportamiento indefinido. Desde C99, sizeof
no es una construcción puramente de tiempo de compilación. El operando de sizeof
se evalúa en tiempo de ejecución si el tipo de operando es un VLA.
Considera el siguiente ejemplo
unsigned n = 10;
int (*a)[n] = NULL; // `a` is a pointer to a VLA
unsigned i = 0;
sizeof a[i++]; // applying `sizeof` to a VLA
De acuerdo con el estándar C99, el argumento de sizeof
se supone que se evalúa (es decir, se supone que i
se incrementa). Sin embargo, no estoy completamente seguro de que la desreferencia de punto nulo en a[0]
deba producir un comportamiento indefinido aquí.
Esto no causará un comportamiento indefinido.
Con la excepción de tomar el tamaño de matrices de longitud variable, sizeof
es una expresión constante en tiempo de compilación. El compilador procesa la expresión para determinar su tipo, sin producir el código para evaluar la expresión en tiempo de compilación. Por lo tanto, el valor en ptr[0]
(que es un puntero sin inicializar) no importa en absoluto.
Además, si desea asignar diez enteros, debería llamar a malloc
esta forma:
int *ptr = malloc(known_len * sizeof(ptr[0]));
De lo contrario, asigna diez bytes, que es demasiado pequeño para almacenar diez enteros. Tenga en cuenta que en la expresión anterior ptr
no está inicializada en el momento de la llamada, lo que está perfectamente bien para sizeof
.
La expresión ptr[0]
no se evaluará en sizeof(ptr[0])
. El tamaño se determinará simplemente utilizando el tipo de ptr[0]
en el momento de la compilación.
C11: 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 constante.
Eso significa que no hay un comportamiento indefinido.