C++
Comportamiento definido por la implementación
Buscar..
Char puede estar sin firmar o firmado
El estándar no especifica si char
debe estar firmado o sin firmar. Diferentes compiladores lo implementan de manera diferente, o podrían permitir cambiarlo usando un interruptor de línea de comando.
Tamaño de los tipos integrales.
Los siguientes tipos se definen como tipos integrales :
-
char
- Tipos enteros firmados
- Tipos de enteros sin signo
-
char16_t
ychar32_t
-
bool
-
wchar_t
Con la excepción de sizeof(char)
/ sizeof(signed char)
/ sizeof(unsigned char)
, que se divide entre § 3.9.1.1 [basic.fundamental / 1] y § 5.3.3.1 [expr.sizeof], y sizeof(bool)
, que está completamente definido por la implementación y no tiene un tamaño mínimo, los requisitos de tamaño mínimo de estos tipos se indican en la sección § 3.9.1 [basic.fundamental] de la norma, y se detallarán a continuación.
Tamaño de char
Todas las versiones de estándar el C ++ especifican, en § 5.3.3.1, que sizeof
rendimientos 1
para unsigned char
, signed char
, y char
(es definido por la implementación si el char
tipo se signed
o unsigned
).
char
es lo suficientemente grande como para representar 256 valores diferentes, para ser adecuado para almacenar unidades de código UTF-8.
Tamaño de los tipos enteros con signo y sin signo
El estándar especifica, en el § 3.9.1.2, que en la lista de tipos enteros con signo estándar , que constan de caracteres con signed char
, short int
, int
, long int
long long int
, cada tipo proporcionará al menos tanto almacenamiento como los anteriores en la lista. Además, como se especifica en el § 3.9.1.3, cada uno de estos tipos tiene un tipo de entero sin signo estándar correspondiente, unsigned char
unsigned short int
unsigned int
, unsigned short int
unsigned int
, unsigned int
unsigned long int
, unsigned long int
unsigned long long int
, que tiene el mismo tamaño y alineación que Su correspondiente tipo firmado. Además, como se especifica en el § 3.9.1.1, char
tiene los mismos requisitos de tamaño y alineación que el signed char
y el unsigned char
.
Antes de C ++ 11, long long
y unsigned long long
no formaban parte oficialmente del estándar C ++. Sin embargo, después de su introducción a C, en C99, muchos compiladores admitieron long long
como un tipo entero con signo extendido , y unsigned long long
como un tipo entero sin signo extendido , con las mismas reglas que los tipos C.
La norma garantiza así que:
1 == sizeof(char) == sizeof(signed char) == sizeof(unsigned char)
<= sizeof(short) == sizeof(unsigned short)
<= sizeof(int) == sizeof(unsigned int)
<= sizeof(long) == sizeof(unsigned long)
<= sizeof(long long) == sizeof(unsigned long long)
Los tamaños mínimos específicos para cada tipo no están dados por la norma. En su lugar, cada tipo tiene un rango mínimo de valores que puede admitir, que, como se especifica en § 3.9.1.3, se hereda del estándar C, en §5.2.4.2.1. El tamaño mínimo de cada tipo se puede inferir aproximadamente de este rango, al determinar el número mínimo de bits requeridos; tenga en cuenta que para cualquier plataforma dada, el rango real admitido de cualquier tipo puede ser mayor que el mínimo. Tenga en cuenta que para los tipos con signo, los rangos corresponden al complemento de uno, no al complemento de dos de uso más común; esto es para permitir que una gama más amplia de plataformas cumpla con el estándar.
Tipo | Rango mínimo | Bits mínimos requeridos |
---|---|---|
signed char | -127 a 127 (- (2 7 - 1) a (2 7 - 1)) | 8 |
unsigned char | 0 a 255 (0 a 2 8 - 1) | 8 |
signed short | -32,767 a 32,767 (- (2 15 - 1) a (2 15 - 1)) | dieciséis |
unsigned short | 0 a 65,535 (0 a 2 16 - 1) | dieciséis |
signed int | -32,767 a 32,767 (- (2 15 - 1) a (2 15 - 1)) | dieciséis |
unsigned int | 0 a 65,535 (0 a 2 16 - 1) | dieciséis |
signed long | -2,147,483,647 a 2,147,483,647 (- (2 31 - 1) a (2 31 - 1)) | 32 |
unsigned long | 0 a 4,294,967,295 (0 a 2 32 - 1) | 32 |
Tipo | Rango mínimo | Bits mínimos requeridos |
---|---|---|
signed long long | -9,223,372,036,854,775,807 a 9,223,372,036,854,775,807 (- (2 63 - 1) a (2 63 - 1)) | 64 |
unsigned long long | 0 a 18,446,744,073,709,551,615 (0 a 2 64 - 1) | 64 |
Como se permite que cada tipo sea mayor que su requisito de tamaño mínimo, los tipos pueden diferir en tamaño entre las implementaciones. El ejemplo más notable de esto es con los modelos de datos de 64 bits LP64 y LLP64, donde los sistemas LLP64 (tales como Windows de 64 bits) tiene 32 bits ints
y long
s, y sistemas de LP64 (tales como Linux de 64 bits) tienen int
s de 32 bits y s de 64 bits de long
. Debido a esto, no se puede suponer que los tipos de enteros tengan un ancho fijo en todas las plataformas.
Si se requieren tipos de enteros con ancho fijo, use tipos del encabezado <cstdint>
, pero tenga en cuenta que el estándar hace que las implementaciones sean compatibles con los tipos de ancho exacto int8_t
, int16_t
, int32_t
, int64_t
, intptr_t
, uint8_t
, uint16_t
, uint32_t
, uint64_t
y uintptr_t
.
Tamaño de char16_t
y char32_t
Los tamaños de char16_t
y char32_t
están definidos por la implementación, como se especifica en el § 5.3.3.1, con las estipulaciones que figuran en el § 3.9.1.5:
char16_t
es lo suficientemente grande como para representar cualquier unidad de código UTF-16, y tiene el mismo tamaño, firmeza y alineación queuint_least16_t
; por lo tanto, se requiere que tenga al menos 16 bits de tamaño.char32_t
es lo suficientemente grande como para representar cualquier unidad de código UTF-32, y tiene el mismo tamaño, firmeza y alineación queuint_least32_t
; por lo tanto, se requiere que tenga al menos 32 bits de tamaño.
Tamaño de bool
El tamaño de bool
está definido en la implementación, y puede o no ser 1
.
Tamaño de wchar_t
wchar_t
, como se especifica en § 3.9.1.5, es un tipo distinto, cuyo rango de valores puede representar cada unidad de código distinta del conjunto de caracteres extendido más grande entre los locales admitidos. Tiene el mismo tamaño, firmeza y alineación que uno de los otros tipos integrales, que se conoce como su tipo subyacente . El tamaño de este tipo está definido por la implementación, como se especifica en el § 5.3.3.1, y puede ser, por ejemplo, al menos 8, 16 o 32 bits; si un sistema admite Unicode, por ejemplo, se requiere que wchar_t
tenga al menos 32 bits (una excepción a esta regla es Windows, donde wchar_t
es de 16 bits por motivos de compatibilidad). Se hereda de la norma C90, ISO 9899: 1990 § 4.1.5, con solo una pequeña redacción.
Dependiendo de la implementación, el tamaño de wchar_t
es a menudo, pero no siempre, de 8, 16 o 32 bits. Los ejemplos más comunes de estos son:
- En sistemas similares a Unix y Unix,
wchar_t
es de 32 bits, y generalmente se usa para UTF-32. - En Windows,
wchar_t
es de 16 bits y se usa para UTF-16. - En un sistema que solo tiene soporte de 8 bits,
wchar_t
es de 8 bits.
Si se desea compatibilidad con Unicode, se recomienda usar char
para UTF-8, char16_t
para UTF-16 o char32_t
para UTF-32, en lugar de usar wchar_t
.
Modelos de datos
Como se mencionó anteriormente, los anchos de los tipos de enteros pueden diferir entre plataformas. Los modelos más comunes son los siguientes, con tamaños especificados en bits:
Modelo | int | long | puntero |
---|---|---|---|
LP32 (2/4/4) | dieciséis | 32 | 32 |
ILP32 (4/4/4) | 32 | 32 | 32 |
LLP64 (4/4/8) | 32 | 32 | 64 |
LP64 (4/8/8) | 32 | 64 | 64 |
Fuera de estos modelos:
- Windows de 16 bits utiliza LP32.
- Los sistemas de 32 bits * nix (Unix, Linux, Mac OSX y otros sistemas operativos similares a Unix) y Windows usan ILP32.
- Windows de 64 bits utiliza LLP64.
- Los sistemas de 64 bits * nix utilizan LP64.
Tenga en cuenta, sin embargo, que estos modelos no se mencionan específicamente en la norma en sí.
Número de bits en un byte
En C ++, un byte es el espacio ocupado por un objeto char
. La cantidad de bits en un byte viene dada por CHAR_BIT
, que se define en climits
y se requiere que sea al menos 8. Mientras que la mayoría de los sistemas modernos tienen bytes de 8 bits, y POSIX requiere que CHAR_BIT
sea exactamente 8, hay algunos sistemas donde CHAR_BIT
es mayor que 8, es decir, un solo byte puede estar compuesto por 8, 16, 32 o 64 bits.
Valor numérico de un puntero
El resultado de convertir un puntero a un entero usando reinterpret_cast
está definido por la implementación, pero "... no es sorprendente para aquellos que conocen la estructura de direccionamiento de la máquina subyacente".
int x = 42;
int* p = &x;
long addr = reinterpret_cast<long>(p);
std::cout << addr << "\n"; // prints some numeric address,
// probably in the architecture's native address format
Del mismo modo, el puntero obtenido por conversión de un entero también está definido por la implementación.
La forma correcta de almacenar un puntero como un entero es usando los tipos uintptr_t
o intptr_t
:
// `uintptr_t` was not in C++03. It's in C99, in <stdint.h>, as an optional type
#include <stdint.h>
uintptr_t uip;
// There is an optional `std::uintptr_t` in C++11
#include <cstdint>
std::uintptr_t uip;
C ++ 11 hace referencia a C99 para la definición uintptr_t
(estándar C99, 6.3.2.3):
un tipo de entero sin signo con la propiedad de que cualquier puntero válido para
void
se puede convertir a este tipo, luego se puede volver a convertir en puntero avoid
, y el resultado se comparará igual al puntero original.
Si bien, para la mayoría de las plataformas modernas, puede asumir un espacio de direcciones plano y que la aritmética en uintptr_t
es equivalente a la aritmética en char *
, es totalmente posible que una implementación realice una transformación al uintptr_t
void *
a uintptr_t
siempre que la transformación pueda se invierte cuando se devuelve desde uintptr_t
a void *
.
Tecnicismos
En los sistemas
intptr_t
XSI (X / Open System Interfaces), se requieren los tiposintptr_t
yuintptr_t
, de lo contrario son opcionales .En el sentido del estándar C, las funciones no son objetos; el estándar C no garantiza que
uintptr_t
pueda contener un puntero de función. De todos modos, la conformidad con POSIX (2.12.3) requiere que:Todos los tipos de punteros de función tendrán la misma representación que el puntero de tipo para anular. La conversión de un puntero de función a void * no alterará la representación. Un valor nulo * resultante de dicha conversión se puede convertir de nuevo al tipo de puntero de función original, utilizando una conversión explícita, sin pérdida de información.
C99 §7.18.1:
Cuando se definen los nombres typedef que difieren solo en la ausencia o presencia de la u inicial, denotarán los tipos correspondientes firmados y no firmados como se describe en 6.2.5; una implementación que provea uno de estos tipos correspondientes también proporcionará el otro.
uintptr_t
podría tener sentido si quiere hacer cosas a los bits del puntero que no puede hacer con sensatez con un entero con signo.
Rangos de tipos numéricos
Los rangos de los tipos de enteros están definidos por la implementación. El encabezado <limits>
proporciona la plantilla std::numeric_limits<T>
que proporciona los valores mínimo y máximo de todos los tipos fundamentales. Los valores satisfacen las garantías proporcionadas por el estándar C a través de los <climits>
y (> = C ++ 11) <cinttypes>
.
-
std::numeric_limits<signed char>::min()
es igual aSCHAR_MIN
, que es menor o igual que -127. -
std::numeric_limits<signed char>::max()
es igual aSCHAR_MAX
, que es mayor o igual a 127. -
std::numeric_limits<unsigned char>::max()
es igual aUCHAR_MAX
, que es mayor o igual a 255. -
std::numeric_limits<short>::min()
es igual aSHRT_MIN
, que es menor o igual que -32767. -
std::numeric_limits<short>::max()
es igual aSHRT_MAX
, que es mayor o igual que 32767. -
std::numeric_limits<unsigned short>::max()
es igual aUSHRT_MAX
, que es mayor o igual a 65535. -
std::numeric_limits<int>::min()
es igual aINT_MIN
, que es menor o igual que -32767. -
std::numeric_limits<int>::max()
es igual aINT_MAX
, que es mayor o igual a 32767. -
std::numeric_limits<unsigned int>::max()
es igual aUINT_MAX
, que es mayor o igual a 65535. -
std::numeric_limits<long>::min()
es igual aLONG_MIN
, que es menor o igual a -2147483647. -
std::numeric_limits<long>::max()
es igual aLONG_MAX
, que es mayor o igual que 2147483647. -
std::numeric_limits<unsigned long>::max()
es igual aULONG_MAX
, que es mayor o igual que 4294967295.
-
std::numeric_limits<long long>::min()
es igual aLLONG_MIN
, que es menor o igual que -9223372036854775807. -
std::numeric_limits<long long>::max()
es igual aLLONG_MAX
, que es mayor o igual que 9223372036854775807. -
std::numeric_limits<unsigned long long>::max()
es igual aULLONG_MAX
, que es mayor o igual que 18446744073709551615.
Para los tipos de punto flotante T
, max()
es el valor finito máximo, mientras que min()
es el valor normalizado positivo mínimo. Se proporcionan miembros adicionales para los tipos de punto flotante, que también están definidos por la implementación pero satisfacen ciertas garantías proporcionadas por el estándar C a través del encabezado <cfloat>
.
- El
digits10
da el número de dígitos decimales de precisión.-
std::numeric_limits<float>::digits10
es igual aFLT_DIG
, que es al menos 6. -
std::numeric_limits<double>::digits10
es igual aDBL_DIG
, que es al menos 10. -
std::numeric_limits<long double>::digits10
es igual aLDBL_DIG
, que es al menos 10.
-
- El miembro
min_exponent10
es el E negativo mínimo tal que 10 a la potencia E es normal.-
std::numeric_limits<float>::min_exponent10
es igual aFLT_MIN_10_EXP
, que es como máximo -37. -
std::numeric_limits<double>::min_exponent10
es igual aDBL_MIN_10_EXP
, que es como máximo -37.std::numeric_limits<long double>::min_exponent10
es igual aLDBL_MIN_10_EXP
, que es como máximo -37.
-
- El miembro
max_exponent10
es el E máximo, de modo que 10 para la potencia E es finito.-
std::numeric_limits<float>::max_exponent10
es igual aFLT_MIN_10_EXP
, que es al menos 37. -
std::numeric_limits<double>::max_exponent10
es igual aDBL_MIN_10_EXP
, que es al menos 37. -
std::numeric_limits<long double>::max_exponent10
es igual aLDBL_MIN_10_EXP
, que es al menos 37.
-
- Si el miembro
is_iec559
es verdadero, el tipo cumple con IEC 559 / IEEE 754 y, por lo tanto, su rango está determinado por esa norma.
Representación del valor de los tipos de punto flotante
El estándar requiere que el long double
proporcione al menos la misma precisión que el double
, lo que proporciona al menos la misma precisión que el float
; y que un long double
puede representar cualquier valor que un double
pueda representar, mientras que un double
puede representar cualquier valor que un float
pueda representar. Los detalles de la representación están, sin embargo, definidos por la implementación.
Para un tipo de punto flotante T
, std::numeric_limits<T>::radix
especifica el radix utilizado por la representación de T
Si std::numeric_limits<T>::is_iec559
es verdadero, entonces la representación de T
coincide con uno de los formatos definidos por IEC 559 / IEEE 754.
Desbordamiento al convertir de entero a entero con signo
Cuando un entero con signo o sin signo se convierte en un tipo de entero con signo y su valor no se puede representar en el tipo de destino, el valor producido se define por la implementación. Ejemplo:
// Suppose that on this implementation, the range of signed char is -128 to +127 and
// the range of unsigned char is 0 to 255
int x = 12345;
signed char sc = x; // sc has an implementation-defined value
unsigned char uc = x; // uc is initialized to 57 (i.e., 12345 modulo 256)
Tipo subyacente (y, por tanto, tamaño) de una enumeración
Si el tipo subyacente no se especifica explícitamente para un tipo de enumeración sin ámbito, se determina de una manera definida por la implementación.
enum E {
RED,
GREEN,
BLUE,
};
using T = std::underlying_type<E>::type; // implementation-defined
Sin embargo, el estándar requiere que el tipo subyacente de una enumeración no sea mayor que int
menos que int
y unsigned int
no puedan representar todos los valores de la enumeración. Por lo tanto, en el código anterior, T
podría ser int
, unsigned int
, o short
, pero no long long
, para dar algunos ejemplos.
Tenga en cuenta que una enumeración tiene el mismo tamaño (según lo devuelto por sizeof
) que su tipo subyacente.