uint32_t uint16_t que c++ c++11 iostream language-lawyer standard-library

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 un signed char o un unsigned 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) se int8_t si int8_t es una coincidencia exacta para signed char (es decir, un typedef de signed char ).
  • De lo contrario, int8_t se promocionaría a int y se basic_ostream<charT,traits>& operator<<(int n) la basic_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) se uint8_t si uint8_t es una coincidencia exacta para unsigned char .
  • De lo contrario, dado que int puede representar todos los valores de uint8_t , uint8_t se promocionará a int y se basic_ostream<charT,traits>& operator<<(int n) la basic_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 tipo int8_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 ancho N , 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 y unsigned char son tres tipos distintos . [...] En cualquier implementación particular, un objeto de char simple puede tomar los mismos valores que un signed char o un unsigned 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 << ...