c++ - uint32_t - uint16_t que es
¿Int8_t y uint8_t están destinados a ser tipos de caracteres? (5)
Dado este programa C ++ 11, ¿debería esperar ver un número o una carta? ¿O no hacer expectativas?
#include <cstdint>
#include <iostream>
int main()
{
int8_t i = 65;
std::cout << i;
}
¿El estándar especifica si este tipo puede o será un tipo de carácter?
Desde § 18.4.1 [cstdint.syn] del C ++ 0x FDIS (N3290), int8_t
es un typedef opcional que se especifica de la siguiente manera:
namespace std {
typedef signed integer type int8_t; // optional
//...
} // namespace std
§ 3.9.1 [estados básicos.fundamentales]:
Hay cinco tipos de entero con signo estándar : "
signed char
", "short int
", "int
", "long int
" y "long long int
". En esta lista, cada tipo proporciona al menos tanto almacenamiento como los que lo preceden en la lista. También puede haber tipos de entero extendido con extensión definida por la implementación. Los tipos enteros con signo estándar y extendido se denominan colectivamente tipos de entero con signo ....
Los tipos
bool
,char
,char16_t
,char32_t
,wchar_t
y los tipos enteros con signo y sin signo se denominan colectivamente tipos integrales . Un sinónimo para tipo integral es tipo entero .
El § 3.9.1 también declara:
En cualquier implementación particular, un objeto de
char
simple puede tomar los mismos valores que unsigned char
o ununsigned char
; cuál es definido por la implementación.
Es tentador concluir que int8_t
puede ser un typedef de caracteres siempre que los objetos char
tomen valores con signo; sin embargo, este no es el caso, ya que char
no se encuentra entre la lista de tipos enteros con signo ( tipos enteros con signo y posiblemente extendidos). Véanse también los comentarios de Stephan T. Lavavej sobre std::make_unsigned
y std::make_signed
.
Por lo tanto, int8_t
es un typedef de signed char
o es un tipo de entero extendido y firmado cuyos objetos ocupan exactamente 8 bits de almacenamiento.
Sin embargo, para responder a su pregunta, no debe hacer suposiciones. Debido a que se han definido las funciones de ambas formas x.operator<<(y)
y operator<<(x,y)
, § 13.5.3 [over.binary] dice que nos referimos a § 13.3.1.2 [over.match.oper ] para determinar la interpretación de std::cout << i
. El § 13.3.1.2 a su vez dice que la implementación selecciona del conjunto de funciones candidatas de acuerdo con § 13.3.2 y § 13.3.3. Luego buscamos en § 13.3.3.2 [over.ics.rank] para determinar que:
- La
template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char)
seint8_t
siint8_t
es una coincidencia exacta parasigned char
(es decir, un typedef designed char
). - De lo contrario,
int8_t
se promocionaría aint
y sebasic_ostream<charT,traits>& operator<<(int n)
labasic_ostream<charT,traits>& operator<<(int n)
.
En el caso de std::cout << u
para u
uint8_t
objeto:
- La
template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char)
seuint8_t
siuint8_t
es una coincidencia exacta paraunsigned char
. - De lo contrario, dado que
int
puede representar todos los valores deuint8_t
,uint8_t
se promocionará aint
y sebasic_ostream<charT,traits>& operator<<(int n)
labasic_ostream<charT,traits>& operator<<(int n)
.
Si siempre deseas imprimir un personaje, la opción más segura y clara es:
std::cout << static_cast<signed char>(i);
Y si siempre quieres imprimir un número:
std::cout << static_cast<int>(i);
El borrador de trabajo que tengo, N3376, especifica en [cstdint.syn] § 18.4.1 que los tipos int son típicamente typedefs.
namespace std {
typedef signed integer type int8_t; // optional
typedef signed integer type int16_t; // optional
typedef signed integer type int32_t; // optional
typedef signed integer type int64_t; // optional
typedef signed integer type int_fast8_t;
typedef signed integer type int_fast16_t;
typedef signed integer type int_fast32_t;
typedef signed integer type int_fast64_t;
typedef signed integer type int_least8_t;
typedef signed integer type int_least16_t;
typedef signed integer type int_least32_t;
typedef signed integer type int_least64_t;
typedef signed integer type intmax_t;
typedef signed integer type intptr_t; // optional
typedef unsigned integer type uint8_t; // optional
typedef unsigned integer type uint16_t; // optional
typedef unsigned integer type uint32_t; // optional
typedef unsigned integer type uint64_t; // optional
typedef unsigned integer type uint_fast8_t;
typedef unsigned integer type uint_fast16_t;
typedef unsigned integer type uint_fast32_t;
typedef unsigned integer type uint_fast64_t;
typedef unsigned integer type uint_least8_t;
typedef unsigned integer type uint_least16_t;
typedef unsigned integer type uint_least32_t;
typedef unsigned integer type uint_least64_t;
typedef unsigned integer type uintmax_t;
typedef unsigned integer type uintptr_t; // optional
} // namespace std
Dado que el único requisito es que debe ser de 8 bits, entonces typedef a un char es aceptable.
Responderé tus preguntas en orden inverso.
¿El estándar especifica si este tipo puede o será un tipo de carácter?
Respuesta corta : int8_t
está signed char
en las plataformas más populares (GCC / Intel / Clang en Linux y Visual Studio en Windows) pero puede ser algo más en otros.
La respuesta larga sigue.
La Sección 18.4.1 del Estándar C ++ 11 proporciona la sinopsis de <cstdint>
que incluye los siguientes
typedef
entero con signo tipoint8_t; //optional
int8_t; //optional
Más adelante en la misma sección, párrafo 2, dice
El encabezado [
<cstdint>
] define todas las funciones, tipos y macros de la misma manera que 7.18 en el estándar C.
donde C estándar significa C99 según 1.1 / 2:
C ++ es un lenguaje de programación de propósito general basado en el lenguaje de programación C como se describe en ISO / IEC 9899: 1999 Lenguajes de programación - C (en lo sucesivo, el estándar C ).
Por lo tanto, la definición de int8_t
se encuentra en la Sección 7.18 del estándar C99. Más precisamente, la sección 7.18.1.1 de C99 dice
El nombre de
typedef
intN_t
designa un tipo de entero con signo con anchoN
, sin bits de relleno y una representación de complemento de dos. Por lo tanto, int8_t denota un tipo entero con signo con un ancho de exactamente 8 bits .
Además, la sección 6.2.5 / 4 de C99 dice
Hay cinco tipos de entero con signo estándar , designados como char firmado , short int , int , long int y long long int . (Estos y otros tipos pueden designarse de varias maneras adicionales, como se describe en 6.7.2.) También puede haber tipos de entero extendido con extensión definida por la implementación . Los tipos enteros con signo estándar y extendido se denominan colectivamente tipos de entero con signo .
Finalmente, la Sección 5.2.4.2.1 de C99 impone tamaños mínimos para los tipos enteros con signo estándar. Excluyendo signed char
, todos los demás tienen al menos 16 bits de longitud.
Por lo tanto, int8_t
tiene el int8_t
signed char
o un tipo entero con signo extendido de 8 bits (no estándar).
Tanto glibc (la biblioteca C de GNU) como la biblioteca Visual Studio C definen int8_t
como signed char
. Intel y Clang, al menos en Linux, también usan libc y, por lo tanto, lo mismo se aplica a ellos. Por lo tanto, en las plataformas más populares int8_t
está signed char
.
Dado este programa C ++ 11, ¿debería esperar ver un número o una carta? ¿O no hacer expectativas?
Respuesta corta : en las plataformas más populares (GCC / Intel / Clang en Linux y Visual Studio en Windows) seguramente verá la letra ''A''. Sin embargo, en otras plataformas puedes ver 65
. (Gracias a dyp por señalarme esto).
En la continuación, todas las referencias son para el estándar C ++ 11 (borrador actual, N3485).
La sección 27.4.1 proporciona la sinopsis de <iostream>
, en particular, establece la declaración de cout
:
extern ostream cout;
Ahora, ostream
es un typedef
para una especialización de plantilla de basic_ostream
según la Sección 27.7.1:
template <class charT, class traits = char_traits<charT> >
class basic_ostream;
typedef basic_ostream<char> ostream;
La Sección 27.7.3.6.4 proporciona la siguiente declaración:
template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c);
Si int8_t
tiene una signed char
se int8_t
esta sobrecarga. La misma sección también especifica que el efecto de esta llamada es imprimir el carácter (no el número).
Ahora, consideremos el caso donde int8_t
es un tipo de entero extendido y firmado. Obviamente, la norma no especifica sobrecargas del operator<<()
para tipos no estándar, pero gracias a promociones y conversiones, una de las sobrecargas proporcionadas podría aceptar la llamada. De hecho, int
tiene al menos 16 bits de longitud y puede representar todos los valores de int8_t
. Entonces 4.5 / 1 da que int8_t
puede ser promovido a int
. Por otro lado, 4.7 / 1 y 4.7 / 2 dan que int8_t
se puede convertir a signed char
. Finalmente, 13.3.3.1.1 produce que la promoción se favorece sobre la conversión durante la resolución de sobrecarga. Por lo tanto, la siguiente sobrecarga (declarada en 23.7.3.1)
basic_ostream & basic_ostream :: operator << (int n);
sera llamado. Esto significa que, este código
int8_t i = 65;
std::cout << i;
imprimirá 65
.
Actualizar:
1 . Corregí la publicación después del comentario de dyp .
2 . Se agregaron los siguientes comentarios sobre la posibilidad de que int8_t
sea un typedef
para char
.
Como se dijo, el estándar C99 (Sección 6.2.5 / 4 citado anteriormente) define 5 tipos de entero con signo estándar ( char
no es uno de ellos) y permite a las implementaciones agregar sus onw que se conocen como tipos de entero con signo no estándar. El estándar de C ++ refuerza esa definición en la Sección 3.9.1 / 2:
Hay cinco tipos de entero con signo estándar: "char firmado", "short int", "int", "long int" y "long long int" [...] También pueden existir tipos enteros con signo extendido definidos por la implementación. Los tipos enteros con signo estándar y extendido se denominan colectivamente tipos de entero con signo .
Más tarde, en la misma sección, el párrafo 7 dice:
Los tipos
bool
,char
,char16_t
,char32_t
,wchar_t
y los tipos enteros con signo y sin signo se denominan colectivamente tipos integrales . Un sinónimo para tipo integral es tipo entero .
Por lo tanto, char
es un tipo entero pero char
no es un tipo entero con signo ni un entero entero sin signo y la Sección 18.4.1 (citada anteriormente) dice que int8_t
, cuando está presente, es un typedef
para un tipo entero con signo.
Lo que podría ser confuso es que, dependiendo de la implementación, char
puede tomar los mismos valores que un signed char
. En particular, char
puede tener un letrero pero aún no es un signed char
. Esto se dice explícitamente en la Sección 3.9.1 / 1:
[...]
char
simple,char
signed char
yunsigned char
son tres tipos distintos . [...] En cualquier implementación particular, un objeto dechar
simple puede tomar los mismos valores que unsigned char
o ununsigned char
; cuál es definido por la implementación.
Esto también implica que char
no es un tipo entero con signo como se define en 3.9.1 / 2.
3 . Admito que mi interpretación y, específicamente, la oración " char
no es un tipo entero con signo ni un tipo entero sin signo" es un poco controvertida.
Para fortalecer mi caso, me gustaría agregar que Stephan T. Lavavej dijo lo mismo aquí y Johannes Schaub - litb también usó la misma oración en un comentario sobre this publicación.
char
/ signed char
/ unsigned char
son tres tipos diferentes, y un char
no es siempre de 8 bits. en la mayoría de las plataformas son todos enteros de 8 bits, pero std :: ostream solo definió la versión char de >>
para comportamientos como scanf("%c", ...)
.
int8_t
tiene exactamente 8 bits de ancho (si existe).
Los únicos tipos enteros predefinidos que pueden ser 8 bits son char
, unsigned char
y signed char
. Tanto el short
como el short
unsigned short
deben tener al menos 16 bits.
Entonces int8_t
debe ser un typedef para signed char
o char
simple (este último si está firmado char
simple).
Si desea imprimir un valor int8_t
como un entero en lugar de como un carácter, puede convertirlo explícitamente a int
.
En principio, un compilador de C ++ podría definir un tipo de entero extendido de 8 bits (tal vez llamado algo como __int8
) y hacer de int8_t
un typedef para él. La única razón por la que se me ocurre hacer eso sería evitar que int8_t
un tipo de carácter. No conozco ningún compilador de C ++ que haya hecho esto.
Tanto int8_t
como los tipos de entero extendido se introdujeron en C99. Para C, no hay una razón particular para definir un tipo de entero extendido de 8 bits cuando los tipos de caracteres están disponibles.
ACTUALIZAR :
No estoy del todo cómodo con esta conclusión. int8_t
y uint8_t
se introdujeron en C99. En C, no importa si son tipos de personajes o no; no hay operaciones para las cuales la distinción hace una diferencia real. (Incluso putc()
, la rutina de salida de caracteres de nivel más bajo en C estándar, toma el carácter que se imprimirá como un argumento int
). int8_t
y uint8_t
, si están definidos, casi seguramente se definirán como tipos de caracteres, pero los tipos de caracteres son solo tipos enteros pequeños.
C ++ proporciona versiones específicas sobrecargadas del operator<<
para char
, signed char
y unsigned char
, de modo que std::cout << ''A''
y std::cout << 65
producen resultados muy diferentes. Más tarde, C ++ adoptó int8_t
y uint8_t
, pero de tal manera que, como en C, casi con seguridad son tipos de caracteres. Para la mayoría de las operaciones, esto no importa más que en C, pero para std::cout << ...
hace la diferencia, ya que esto:
uint8_t x = 65;
std::cout << x;
probablemente imprima la letra A
lugar del número 65
.
Si quieres un comportamiento consistente, agrega un elenco:
uint8_t x = 65;
std::cout << int(x); // or static_cast<int>(x) if you prefer
Creo que la raíz del problema es que falta algo en el lenguaje: tipos enteros muy estrechos que no son tipos de caracteres.
En cuanto a la intención , podría especular que los miembros del comité o no pensaron sobre el tema o decidieron que no valía la pena abordarlo. Se podría argumentar (y lo haría) que los beneficios de agregar los tipos [u]int*_t
al estándar superan la inconveniencia de su comportamiento bastante extraño con std::cout << ...