Существуют ли типы 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", ...)
.