the number length know calculate array c arrays sizeof c99 undefined-behavior

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 tipo void * , 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 de ptr se conoce el valor de ptr ?
  • ¿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.