c gcc mingw size-t stdint

c - ¿Por qué el tamaño máximo de una matriz es "demasiado grande"?



gcc mingw (4)

El límite SIZE_MAX / 2 proviene de las definiciones de size_t y ptrdiff_t en su implementación, que eligen que los tipos ptrdiff_t y size_t tengan el mismo ancho.

C El estándar 1 exige que el tipo size_t no esté firmado y el tipo ptrdiff_t esté firmado.

El resultado de la diferencia entre dos punteros siempre tendrá el tipo ptrdiff_t. Esto significa que, en su implementación, el tamaño del objeto debe limitarse a PTRDIFF_MAX, de lo contrario, una diferencia válida de dos punteros no podría representarse en el tipo ptrdiff_t, lo que lleva a un comportamiento indefinido.

Por lo tanto, el valor SIZE_MAX / 2 es igual al valor PTRDIFF_MAX. Si la implementación elige que el tamaño máximo del objeto sea SIZE_MAX, entonces el ancho del tipo ptrdiff_t debería incrementarse. Pero es mucho más fácil limitar el tamaño máximo del objeto a SIZE_MAX / 2, entonces es hacer que el tipo ptrdiff_t tenga un rango positivo mayor o igual que el del tipo size_t.

Standard ofrece estos 3 comentarios 4 sobre el tema.

(Citado de ISO / IEC 9899: 201x)

1 (7.19 Definiciones comunes 2)
Los tipos son
ptrdiff_t
cuál es el tipo entero con signo del resultado de restar dos punteros;
tamaño_t
cuál es el tipo entero sin signo del resultado del operador sizeof;

2 (6.5.6 Operadores aditivos 9)
Cuando se restan dos punteros, ambos deben apuntar a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz; El resultado es la diferencia de los subíndices de los dos elementos de la matriz. El tamaño del resultado está definido por la implementación, y su tipo (un tipo entero con signo) se define ptrdiff_t en el encabezado. Si el resultado no es representable en un objeto de ese tipo, el comportamiento es indefinido.

3 (K.3.4 Tipos enteros 3)
Los tamaños de objeto extremadamente grandes son con frecuencia una señal de que el tamaño de un objeto se calculó incorrectamente. Por ejemplo, los números negativos aparecen como números positivos muy grandes cuando se convierten a un tipo sin signo como size_t. Además, algunas implementaciones no admiten objetos tan grandes como el valor máximo que puede representarse mediante el tipo size_t.

4 (K.3.4 Tipos enteros 4)
Por esas razones, a veces es beneficioso restringir el rango de tamaños de objeto para detectar errores de programación. Para implementaciones dirigidas a máquinas con grandes espacios de direcciones, se recomienda que RSIZE_MAX se defina como el tamaño más pequeño del objeto más grande admitido o (SIZE_MAX >> 1), incluso si este límite es más pequeño que el tamaño de algunos legítimos, pero muy grandes objetos. Las implementaciones dirigidas a máquinas con pequeños espacios de direcciones pueden desear definir RSIZE_MAX como SIZE_MAX, lo que significa que no hay un tamaño de objeto que se considere una violación de restricción de tiempo de ejecución.

Tengo la misma impresión que esta respuesta , que size_t siempre está garantizado por el estándar para ser lo suficientemente grande como para contener el tipo más grande posible de un sistema dado.

Sin embargo, este código no se compila en gcc / Mingw:

#include <stdint.h> #include <stddef.h> typedef uint8_t array_t [SIZE_MAX];

error: el tamaño de la matriz ''array_t'' es demasiado grande

¿Estoy malinterpretando algo en el estándar aquí? ¿Se permite que size_t sea ​​demasiado grande para una implementación dada? ¿O es este otro error en Mingw?

EDITAR: más investigaciones muestran que

typedef uint8_t array_t [SIZE_MAX/2]; // does compile typedef uint8_t array_t [SIZE_MAX/2+1]; // does not compile

Que resulta ser lo mismo que

#include <limits.h> typedef uint8_t array_t [LLONG_MAX]; // does compile typedef uint8_t array_t [LLONG_MAX+(size_t)1]; // does not compile

Así que ahora me inclino a creer que esto es un error en Mingw, porque establecer el tamaño máximo permitido basado en un tipo entero con signo no tiene ningún sentido.


Se garantiza que el rango de size_t es suficiente para almacenar el tamaño del objeto más grande admitido por la implementación. Lo contrario no es cierto: no se garantiza que pueda crear un objeto cuyo tamaño llene todo el rango de size_t .

En tales circunstancias, la pregunta es: ¿qué significa SIZE_MAX ? ¿El tamaño de objeto admitido más grande? ¿O el mayor valor representable en size_t ? La respuesta es: es la última, es decir, SIZE_MAX es (size_t) -1 . No se garantiza que pueda crear objetos de SIZE_MAX bytes de gran tamaño.

La razón detrás de eso es que, además de size_t , las implementaciones también deben proporcionar ptrdiff_t , que está destinado (pero no está garantizado) a almacenar la diferencia entre dos punteros que apuntan al mismo objeto de matriz. Como el tipo ptrdiff_t está firmado, las implementaciones se enfrentan a las siguientes opciones:

  1. Permitir objetos de matriz de tamaño SIZE_MAX y hacer ptrdiff_t más ancho que size_t . Tiene que ser más ancho al menos un bit. Tal ptrdiff_t puede acomodar cualquier diferencia entre dos punteros apuntando a una matriz de tamaño SIZE_MAX o menor.

  2. Permita objetos de matriz de tamaño SIZE_MAX y use ptrdiff_t del mismo ancho que size_t . Acepte el hecho de que la sustracción del puntero puede desbordarse y causar un comportamiento indefinido, si los punteros están más lejos que SIZE_MAX / 2 elementos separados. La especificación del lenguaje no prohíbe este enfoque.

  3. Use ptrdiff_t del mismo ancho que size_t y restrinja el tamaño máximo del objeto de matriz por SIZE_MAX / 2 . Tal ptrdiff_t puede acomodar cualquier diferencia entre dos punteros apuntando a una matriz de tamaño SIZE_MAX / 2 o menor.

Simplemente se trata de una implementación que decidió seguir el tercer enfoque.


Se parece mucho al comportamiento específico de la implementación.

Estoy ejecutando aquí Mac OS, y con gcc 6.3.0 el tamaño más grande con el que puedo compilar su definición es SIZE_MAX/2 ; con SIZE_MAX/2 + 1 ya no se compila.

Por otro lado, witch clang 4.0.0 el más grande es SIZE_MAX/8 , y SIZE_MAX/8 + 1 saltos.


Solo razonando desde cero, size_t es un tipo que puede contener el tamaño de cualquier objeto. El tamaño de cualquier objeto está limitado por el ancho del bus de direcciones (ignorando la multiplexación y los sistemas que pueden manejar, por ejemplo, código de 32 y 64 bits, llamar a eso "ancho de código"). Análogo a MAX_INT que es el mayor valor entero, SIZE_MAX es el mayor valor de size_t . Por lo tanto, un objeto de tamaño SIZE_MAX es toda memoria direccionable. Es razonable que una implementación marque eso como un error, sin embargo, estoy de acuerdo en que es un error solo en el caso de que se asigne un objeto real, ya sea en la pila o en la memoria global. (Una llamada a malloc por esa cantidad fallará de todos modos)