Существуют ли типы int8_t и uint8_t типа char?

Учитывая эту программу С++ 11, я должен ожидать увидеть номер или письмо? Или не оправдывать ожидания?

#include <cstdint>
#include <iostream>

int main()
{
    int8_t i = 65;
    std::cout << i;
}

Указывает ли стандарт, является ли этот тип типом символа или он будет?

Ответ 1

Из § 18.4.1 [cstdint.syn] из С++ 0x FDIS (N3290), int8_t является необязательным typedef, который указан следующим образом:

namespace std {
  typedef signed integer type int8_t;  // optional
  //...
} // namespace std

§ 3.9.1 [basic.fundamental]:

Существует пять стандартных стандартных целочисленных типов: "signed char", "short int", "int", "long int" и "long long int". В этом списке каждый тип содержит как минимум столько же памяти, сколько и предшествующие ему в списке. Могут также существовать расширенные знаковые целочисленные типы с реализацией. Стандартные и расширенные типы со знаком целого типа называются целыми типами со знаком.

...

Типы bool, char, char16_t, char32_t, wchar_t, а целочисленные типы с подписью и без знака являются коллективно называемыми интегральными типами. Синоним интегрального типа - целочисленный тип.

В § 3.9.1 также говорится:

В любой конкретной реализации простой объект char может принимать те же значения, что и signed char или unsigned char; какой из них определяется реализацией.

Заманчиво заключить, что int8_t может быть typedef из char при условии, что объекты char принимают подписанные значения; однако это не так, поскольку char не входит в список со знаком целочисленных типов (стандартные и, возможно, расширенные целые типы со знаком). См. Также комментарии Stephan T. Lavavej по std::make_unsigned и std::make_signed.

Следовательно, либо int8_t является typedef из signed char, либо представляет собой расширенный целочисленный тип со знаком, объекты которого занимают ровно 8 бит памяти.

Чтобы ответить на ваш вопрос, вы не должны делать предположений. Поскольку функции обеих форм x.operator<<(y) и operator<<(x,y) определены, в § 13.5.3 [over.binary] говорится, что для определения интерпретации std::cout << i мы ссылаемся на § 13.3.1.2 [over.match.oper]. В § 13.3.1.2, в свою очередь, говорится, что реализация выбирает из набора функций-кандидатов согласно § 13.3.2 и § 13.3.3. Затем мы рассмотрим § 13.3.3.2 [over.ics.rank], чтобы определить, что:

  • Шаблон template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char) будет вызываться, если int8_t - это точное соответствие для signed char (т.е. typedef из signed char).
  • В противном случае int8_t будет продвигаться до int, и будет вызываться функция-член basic_ostream<charT,traits>& operator<<(int n).

В случае std::cout << u для объекта u a uint8_t:

  • Шаблон template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char) будет вызываться, если uint8_t - это точное соответствие для unsigned char.
  • В противном случае, поскольку int может представлять все значения uint8_t, uint8_t будет продвигаться до int, и будет вызываться функция-член basic_ostream<charT,traits>& operator<<(int n).

Если вы всегда хотите напечатать символ, самым безопасным и наиболее понятным вариантом является:

std::cout << static_cast<signed char>(i);

И если вы всегда хотите напечатать номер:

std::cout << static_cast<int>(i);

Ответ 2

int8_t имеет ширину в 8 бит (если она существует).

Единственными предопределенными целыми типами, которые могут быть 8 бит, являются char, unsigned char и signed char. И short, и unsigned short должны быть не менее 16 бит.

Итак, int8_t должен быть typedef для signed char или plain char (последний, если plain char подписан).

Если вы хотите напечатать значение int8_t как целое, а не как символ, вы можете явно преобразовать его в int.

В принципе, компилятор С++ может определить 8-разрядный расширенный целочисленный тип (возможно, названный как-то вроде __int8) и сделать для него int8_t typedef. Единственная причина, по которой я могу это сделать, - не допускать тип символа int8_t. Я не знаю каких-либо компиляторов С++, которые действительно это сделали.

Оба типа int8_t и расширенные целочисленные типы были введены на C99. Для C нет особых причин для определения 8-разрядного расширенного целочисленного типа, когда доступны типы char.

UPDATE

Я не совсем понимаю этот вывод. int8_t и uint8_t были введены в C99. В C не имеет особого значения, являются ли они типами символов или нет; нет никаких операций, для которых различие имеет реальное значение. (Даже putc(), процедура вывода символа нижнего уровня в стандартном C, принимает символ, который будет напечатан как аргумент int). int8_t и uint8_t, если они определены, почти наверняка будут определены как типы символов, но типы символов - это только маленькие целые типы.

С++ предоставляет конкретные перегруженные версии operator<< для char, signed char и unsigned char, так что std::cout << 'A' и std::cout << 65 производят очень разный вывод. Позже С++ принял int8_t и uint8_t, но таким образом, что, как и в C, они почти наверняка являются типами символов. Для большинства операций это не имеет значения, чем в C, но для std::cout << ... это имеет значение, так как это:

uint8_t x = 65;
std::cout << x;

вероятно, напечатает букву A, а не цифру 65.

Если вам требуется последовательное поведение, добавьте бросок:

uint8_t x = 65;
std::cout << int(x); // or static_cast<int>(x) if you prefer

Я думаю, что корень проблемы в том, что там что-то отсутствует на языке: очень узкие целые типы, которые не являются символьными типами.

Что касается намерения, я мог бы предположить, что члены комитета либо не думали о проблеме, либо решили, что это не стоит рассматривать. Можно было бы утверждать (и я бы), что преимущества добавления типов [u]int*_t к стандарту перевешивают неудобства их довольно странного поведения с помощью std::cout << ....

Ответ 3

Рабочий черновик проекта I, N3376, указывает в [cstdint.syn] § 18.4.1, что типы int типично типизированы.

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

Поскольку требуется только одно требование, оно должно быть 8 бит, тогда допустим typedef для char.

Ответ 4

Я отвечу на ваши вопросы в обратном порядке.

Указывает ли стандарт, является ли этот тип типом символа или он будет?

Короткий ответ: int8_t является signed char на самых популярных платформах (GCC/Intel/Clang на Linux и Visual Studio для Windows), но может быть что-то еще в других.

Далее следует длинный ответ.

В разделе 18.4.1 стандарта С++ 11 приведен краткий обзор <cstdint>, который включает следующие

typedef целочисленный тип со знаком int8_t; //optional

Далее в том же параграфе, в пункте 2, говорится:

Заголовок [<cstdint>] определяет все функции, типы и макросы такими же, как 7.18 в C.

где C означает C99 согласно 1.1/2:

С++ - это язык программирования общего назначения, основанный на языке программирования C, как описано в ISO/IEC 9899: 1999 Языки программирования - C (далее называемый стандартом C).

Следовательно, определение int8_t содержится в разделе 7.18 стандарта C99. Точнее, в разделе C99 раздела 7.18.1.1 говорится:

typedef name intN_t обозначает знаковый целочисленный тип с шириной N, без битов дополнений и двухкомпонентное представление. Таким образом, int8_t обозначает знаковый целочисленный тип с шириной ровно 8 бит.

Кроме того, в разделе C99 раздела 6.2.5/4 говорится:

Существует пять стандартных стандартных знаковых типов , обозначенных как подписанный char, short int, int, long int и long long int. (Эти и другие типы могут быть назначены несколькими дополнительными способами, как описано в 6.7.2.) Могут также быть расширенные для реализации расширенные типы целочисленных подписей. стандартные и расширенные целочисленные со знаком подписываются совместно именными целыми типами.

Наконец, C99 Раздел 5.2.4.2.1 устанавливает минимальные размеры для стандартных стандартных целых типов. Исключая signed char, все остальные имеют длину не менее 16 бит.

Следовательно, int8_t является либо signed char, либо длинным расширенным (нестандартным) символом со знаком длиной 8 бит.

Оба glibc (библиотека GNU C) и библиотека Visual Studio C определяют int8_t как signed char. Intel и Clang, по крайней мере, на Linux, также используют libc и, следовательно, то же самое относится к ним. Поэтому на самых популярных платформах int8_t есть signed char.

Учитывая эту программу С++ 11, я должен ожидать увидеть номер или письмо? Или не оправдывать ожидания?

Короткий ответ. На самых популярных платформах (GCC/Intel/Clang на Linux и Visual Studio в Windows) вы обязательно увидите букву "A". На других платформах вы можете увидеть 65. (Спасибо DyP за то, что указали это мне.)

В дальнейшем все ссылки относятся к стандарту С++ 11 (текущий проект, N3485).

В разделе 27.4.1 приведен краткий обзор <iostream>, в частности, он содержит декларацию cout:

extern ostream cout;

Теперь ostream является typedef для специализированного шаблона basic_ostream в соответствии с разделом 27.7.1:

template <class charT, class traits = char_traits<charT> >
class basic_ostream;

typedef basic_ostream<char> ostream;

В разделе 27.7.3.6.4 представлено следующее выражение:

template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c);

Если int8_t - signed char, то это перегрузка, которая будет вызвана. В том же разделе также указано, что эффект этого вызова заключается в печати символа (а не числа).

Теперь рассмотрим случай, когда int8_t - это расширенный целочисленный тип со знаком. Очевидно, что стандарт не указывает перегрузки operator<<() для нестандартных типов, но благодаря рекламным акциям и конверсиям одна из предоставленных перегрузок может принять вызов. Действительно, int имеет длину не менее 16 бит и может представлять все значения int8_t. Тогда 4.5/1 дает, что int8_t можно повысить до int. С другой стороны, 4.7/1 и 4.7/2 дают, что int8_t можно преобразовать в signed char. Наконец, 13.3.3.1.1 дает преимущество в продвижении по сравнению с конверсией во время разрешения перегрузки. Поэтому следующая перегрузка (объявленная в пункте 23.7.3.1)

basic_ostream & basic_ostream:: operator < (int n);

. Это означает, что этот код

int8_t i = 65;
std::cout << i;

напечатает 65.

Update:

1. Исправлено сообщение, следующее за комментарием DyP.

2, Добавлены следующие комментарии о возможности int8_t быть a typedef для char.

Как сказано выше, стандарт C99 (раздел 6.2.5/4, приведенный выше) определяет 5 стандартных стандартных целочисленных типов (char не является одним из них) и позволяет реализациям добавлять свои onw, которые упоминаются как нестандартное целое число со знаком типы. Стандарт С++ подтверждает это определение в разделе 3.9.1/2:

Существует пять стандартных стандартных целочисленных типов: "подписанный char", "короткий int", "int", "long int" и "long long int" [...]. Также может быть определена реализация расширенные знаковые целочисленные типы. Стандартный и расширенный целочисленные со знаком подписываются под общим названием со знаком целочисленных типов.

Позже, в том же разделе, в пункте 7 говорится:

Типы bool, char, char16_t, char32_t, wchar_t, , а целые типы с подписью и без знака называются интегральными типами, Синоним интегрального типа целочисленный тип.

Следовательно, char является целым типом, но char не является ни знаковым целым типом, ни целым целым числом без знака, а в разделе 18.4.1 (цитируется выше) говорится, что int8_t, когда present, является typedef для знакового целочисленного типа.

Что может сбить с толку, так это то, что в зависимости от реализации char может принимать те же значения, что и signed char. В частности, char может иметь знак, но он все еще не является signed char. Это прямо сказано в разделе 3.9.1/1:

[...] Обычная char, signed char и unsigned char три различных типа. [...] В любой конкретной реализации простой объект char может принимать те же значения, что и signed char или unsigned char; какой из них определяется реализацией.

Это также означает, что char не знаковый целочисленный тип, определенный в 3.9.1/2.

3. Я признаю, что моя интерпретация и, в частности, предложение "char не является ни знаковым целым типом, ни целым целым числом без знака" является немного противоречивым.

Чтобы усилить мой случай, я хотел бы добавить, что Стефан Т. Лававей сказал то же самое здесь и Johannes Schaub - litb также использовал то же предложение в комментарии к сообщению this.

Ответ 5

char/signed char/unsigned char - три разных типа, а char не всегда 8 бит. на большинстве платформ все они состоят из 8 бит, но std:: ostream определяет только char версию >> для таких типов поведения, как scanf("%c", ...).