c++ - ssize_t - unsigned int vs. size_t
size_t c (8)
Me doy cuenta de que el código moderno de C y C ++ parece usar size_t
lugar de int
/ unsigned int
casi todas partes, desde los parámetros de las funciones de cadena C a la STL. Tengo curiosidad por la razón de esto y los beneficios que trae.
Classic C (el dialecto temprano de C descrito por Brian Kernighan y Dennis Ritchie en The C Programming Language, Prentice-Hall, 1978) no proporcionó size_t
. El comité de estándares de C introdujo size_t
para eliminar un problema de portabilidad
Explicado en detalle en embedded.com (con un muy buen ejemplo)
El tipo size_t
es el tipo entero sin signo que es el resultado del operador sizeof
(y el operador offsetof
), por lo que se garantiza que sea lo suficientemente grande como para contener el tamaño del objeto más grande que su sistema puede manejar (por ejemplo, una matriz estática de 8Gb).
El tipo size_t
puede ser mayor, igual o menor que un unsigned int
, y su compilador puede hacer suposiciones sobre él para la optimización.
Puede encontrar información más precisa en el estándar C99, sección 7.17, cuyo borrador está disponible en Internet en formato pdf , o en el estándar C11, sección 7.19, también disponible como borrador en pdf .
El tipo size_t debe ser lo suficientemente grande para almacenar el tamaño de cualquier objeto posible. Sin firma int no tiene que satisfacer esa condición.
Por ejemplo, los sistemas de 64 bits int y unsigned int pueden tener 32 bits de ancho, pero size_t debe ser lo suficientemente grande para almacenar números mayores a 4G
El tipo size_t es el tipo devuelto por el operador sizeof. Es un entero sin signo capaz de expresar el tamaño en bytes de cualquier rango de memoria admitido en la máquina host. Está (típicamente) relacionado con ptrdiff_t en que ptrdiff_t es un valor entero con signo tal que sizeof (ptrdiff_t) y sizeof (size_t) son iguales.
Al escribir el código C, siempre debe usar size_t cuando se trate de rangos de memoria.
Por otro lado, el tipo int se define básicamente como el tamaño del valor entero (con signo) que la máquina host puede usar para realizar la aritmética de enteros de manera más eficiente. Por ejemplo, en muchas computadoras de tipo PC antiguas, el valor sizeof (size_t) sería 4 (bytes) pero sizeof (int) sería 2 (byte). La aritmética de 16 bits era más rápida que la aritmética de 32 bits, aunque la CPU podía manejar un espacio de memoria (lógico) de hasta 4 GiB.
Utilice el tipo int solo cuando le importa la eficiencia, ya que su precisión real depende en gran medida de las opciones del compilador y de la arquitectura de la máquina. En particular, el estándar C especifica los siguientes invariantes: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) que no coloca ninguna otra limitación en la representación real de la precisión disponible para el programador para cada uno de estos tipos primitivos.
Nota: Esto NO es lo mismo que en Java (que en realidad especifica la precisión de bit para cada uno de los tipos ''char'', ''byte'', ''short'', ''int'' y ''long'').
En resumen, size_t
nunca es negativo, y maximiza el rendimiento porque se define como el tipo entero sin signo que es lo suficientemente grande, pero no demasiado grande, para representar el tamaño del objeto más grande posible en la plataforma de destino.
Los tamaños nunca deben ser negativos, y de hecho size_t
es un tipo sin signo. Además, debido a que size_t
no está firmado, puede almacenar números que son aproximadamente dos veces más grandes que en el tipo con signo correspondiente, porque podemos usar el bit de signo para representar la magnitud, como todos los otros bits en el entero sin signo. Cuando ganamos un bit más, estamos multiplicando el rango de números que podemos representar por un factor de aproximadamente dos.
Entonces, preguntas, ¿por qué no usar simplemente un unsigned int
? Puede que no sea capaz de mantener grandes números suficientes. En una implementación donde el unsigned int
es de 32 bits, el número más grande que puede representar es 4294967295
. Algunos procesadores, como el IP16L32, pueden copiar objetos de más de 4294967295
bytes.
Entonces, preguntas, ¿por qué no usar un unsigned long int
? Exige un peaje de rendimiento en algunas plataformas. El estándar C requiere que una long
ocupe al menos 32 bits. Una plataforma IP16L32 implementa cada una de 32 bits como un par de palabras de 16 bits. Casi todos los operadores de 32 bits en estas plataformas requieren dos instrucciones, si no más, porque funcionan con los 32 bits en dos fragmentos de 16 bits. Por ejemplo, mover un largo de 32 bits generalmente requiere dos instrucciones de la máquina, una para mover cada fragmento de 16 bits.
El uso de size_t
evita este peaje de rendimiento. De acuerdo con este fantástico artículo , "Type size_t
es un typedef que es un alias para algún tipo de entero sin signo, normalmente unsigned int
o unsigned long
, pero posiblemente unsigned long long
. Se supone que cada implementación de Standard C elige el entero sin signo que sea lo suficientemente grande. -pero no más grande de lo necesario - para representar el tamaño del objeto más grande posible en la plataforma de destino ".
Este extracto del manual de glibc 0.02 también puede ser relevante cuando se investiga el tema:
Existe un problema potencial con el tipo size_t y las versiones de GCC antes de la versión 2.4. ANSI C requiere que size_t siempre sea un tipo sin signo. Para la compatibilidad con los archivos de encabezado de los sistemas existentes, GCC define size_t en stddef.h'' to be whatever type the system''s
sys / types.h'' del sistema define como. La mayoría de los sistemas Unix que definen size_t en `sys / types.h '', lo definen como un tipo firmado. Algunos códigos en la biblioteca dependen de que size_t sea un tipo sin firma y no funcionará correctamente si está firmado.
El código de la biblioteca GNU C que espera que size_t no esté firmado es correcto. La definición de size_t como tipo firmado es incorrecta. Planeamos que en la versión 2.4, GCC siempre definirá size_t como un tipo sin firma, y el fixincludes'' script will massage the system''s
sys / types.h'' del sistema para no entrar en conflicto con esto.
Mientras tanto, resolvemos este problema diciéndole a GCC explícitamente que use un tipo sin signo para size_t al compilar la biblioteca GNU C. `configure ''detectará automáticamente qué tipo de GCC usa para size_t organizar para anularlo si es necesario.
Si mi compilador está configurado a 32 bits, size_t
no es otra cosa que typedef para unsigned int
. Si mi compilador se establece en 64 bits, size_t
no es más que un typedef para unsigned long long
.
size_t es el tamaño de un puntero.
Por lo tanto, en 32 bits o el modelo común ILP32 (entero, largo, puntero) size_t es de 32 bits. y en 64 bits o el modelo común LP64 (largo, puntero) size_t es de 64 bits (los enteros siguen siendo de 32 bits).
Hay otros modelos, pero estos son los que utiliza g ++ (al menos de forma predeterminada)