Предназначены ли int8_t и uint8_t для типов символов?
Учитывая эту программу на C++11, я должен ожидать увидеть число или букву? Или не делать ожидания?
#include <cstdint>
#include <iostream>
int main()
{
int8_t i = 65;
std::cout << i;
}
Стандарт определяет, может ли этот тип быть или будет типом символа?
5 ответов
From § 18.4.1 [cstdint.syn] of the C++0x FDIS (N3290), int8_t
is an optional typedef that is specified as follows:
namespace std {
typedef signed integer type int8_t; // optional
//...
} // namespace std
§ 3.9.1 [basic.fundamental] states:
There are five standard signed integer types: “
signed char
","short int
","int
","long int
”, and “long long int
". В этом списке каждый тип обеспечивает как минимум столько же памяти, сколько предшествует ему в списке. There may also be implementation-defined extended signed integer types. The standard and extended signed integer types are collectively called signed integer types....
Типы
bool
,char
,char16_t
,char32_t
,wchar_t
, and the signed and unsigned integer types are collectively called integral types. A synonym for integral type is integer type.
§ 3.9.1 also states:
In any particular implementation, a plain
char
object can take on either the same values as asigned char
илиunsigned char
; какой из них определяется реализацией.
It is tempting to conclude that int8_t
may be a typedef of char
предоставлена char
objects take on signed values; however, this is not the case as char
не входит в список целочисленных типов со знаком (стандартные и, возможно, расширенные целочисленные типы со знаком). Смотрите также комментарии Стефана Т. Лававея на std::make_unsigned
а также std::make_signed
,
Поэтому либо int8_t
является определением типа signed char
или это расширенный целочисленный тип со знаком, чьи объекты занимают ровно 8 бит памяти.
Чтобы ответить на ваш вопрос, вы не должны делать предположений. Потому что функции обеих форм x.operator<<(y)
а также operator<<(x,y)
были определены, § 13.5.3 [over.binary] говорит, что мы обращаемся к § 13.3.1.2 [over.match.oper], чтобы определить интерпретацию std::cout << i
, В п. 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
(т.е. определение типаsigned char
).- В противном случае
int8_t
будет повышен доint
иbasic_ostream<charT,traits>& operator<<(int n)
функция-член будет вызвана.
В случае std::cout << u
за u
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);
int8_t
ровно 8 бит в ширину (если он существует).
Единственными предопределенными целочисленными типами, которые могут быть 8 битами, являются char
, unsigned char
, а также signed char
, И то и другое short
а также unsigned short
должны быть не менее 16 бит.
Так int8_t
должен быть typedef для любого signed char
или простой char
(последний, если обычный char
подписано).
Если вы хотите распечатать int8_t
значение как целое число, а не как символ, вы можете явно преобразовать его в int
,
В принципе, компилятор C++ может определять 8-битный расширенный целочисленный тип (возможно, называемый что-то вроде __int8
), и сделать int8_t
typedef для этого. Единственная причина, по которой я могу это сделать, - это избегать int8_t
тип персонажа. Я не знаю ни одного компилятора C++, который бы на самом деле сделал это.
И то и другое int8_t
и расширенные целочисленные типы были введены в C99. Для C нет особой причины определять 8-битный расширенный целочисленный тип, когда char
типы доступны.
ОБНОВЛЕНИЕ:
Я не совсем доволен этим выводом. int8_t
а также uint8_t
были введены в C99. В C не имеет особого значения, являются ли они типами символов или нет; нет операций, для которых различие имеет реальное значение. (Четное putc()
, процедура вывода символов самого низкого уровня в стандарте C, принимает символ для печати как int
аргумент). int8_t
, а также uint8_t
если они определены, почти наверняка будут определены как символьные типы - но символьные типы - это просто маленькие целочисленные типы.
C++ предоставляет конкретные перегруженные версии operator<<
за char
, signed char
, а также unsigned char
, чтобы std::cout << 'A'
а также std::cout << 65
производить очень разные результаты. Позже, C++ принял 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 << ...
,
Я отвечу на ваши вопросы в обратном порядке.
Стандарт определяет, может ли этот тип быть или будет типом символа?
Краткий ответ: int8_t
является signed char
на самых популярных платформах (GCC/Intel/Clang в Linux и Visual Studio в Windows), но могут быть и другими.
Длинный ответ следует.
Раздел 18.4.1 стандарта C++11 содержит краткий обзор <cstdint>
который включает в себя следующее
typedef
целочисленный тип со знакомint8_t; //optional
Позже в том же разделе, параграф 2, говорится
Заголовок [
<cstdint>
] определяет все функции, типы и макросы так же, как 7.18 в стандарте C.
где C стандарт означает C99 согласно 1.1 / 2:
C++ - это язык программирования общего назначения, основанный на языке программирования C, как описано в ISO/IEC 9899:1999 Языки программирования - C (в дальнейшем именуемый стандартом C).
Отсюда и определение int8_t
находится в разделе 7.18 стандарта C99. Точнее, в разделе 7.18.1.1 C99 говорится
typedef
названиеintN_t
обозначает целочисленный тип со знаком с ширинойN
, нет дополнительных битов, и представление дополнения до двух. Таким образом, int8_t обозначает целочисленный тип со знаком шириной ровно 8 бит.
Кроме того, в разделе 6.2.5/4 раздела C99 говорится
Существует пять стандартных целочисленных типов со знаком, обозначаемых как char со знаком, short int, int, long int и long long int. (Эти и другие типы могут быть обозначены несколькими дополнительными способами, как описано в 6.7.2.) Также могут быть определенные типом расширенные знаковые целочисленные типы. Стандартные и расширенные целочисленные типы со знаком вместе называются целочисленными типами со знаком.
Наконец, в разделе 5.2.4.2.1 раздела C99 установлены минимальные размеры для стандартных целочисленных типов со знаком. Исключая 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
,
Учитывая эту программу на C++11, я должен ожидать увидеть число или букву? Или не делать ожидания?
Краткий ответ: на самых популярных платформах (GCC/Intel/Clang в Linux и Visual Studio в Windows) вы обязательно увидите букву "A". На других платформах вы можете увидеть 65
хоть. (Спасибо dyp за указание на это мне.)
В дальнейшем все ссылки на стандарт C++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
,
Обновить:
1 Исправлено сообщение после комментария dyp.
2 Добавлены следующие комментарии о возможности int8_t
быть typedef
за char
,
Как уже говорилось, стандарт C99 (приведенный выше раздел 6.2.5/4) определяет 5 стандартных целочисленных типов со знаком (char
не является одним из них) и позволяет реализациям добавлять свои onw, которые называются нестандартными целочисленными типами со знаком. Стандарт C++ подкрепляет это определение в Разделе 3.9.1/2:
Существует пять стандартных целочисленных типов со знаком: "знаковый символ", "короткое целое", "int", "длинное целое" и "длинное длинное целое" [...]. Также могут существовать типы расширенных целых чисел со знаком, определенные реализацией. Стандартные и расширенные целочисленные типы со знаком вместе называются целочисленными типами со знаком.
Позже, в том же разделе, пункт 7 гласит:
Типы
bool
,char
,char16_t
,char32_t
,wchar_t
и целочисленные типы со знаком и без знака вместе называются целочисленными типами. Синоним для целочисленного типа - целочисленный тип.
Следовательно, char
целочисленный тип, но char
не является ни целочисленным типом со знаком, ни целочисленным типом без знака, и в Разделе 18.4.1 (цитируется выше) говорится, что int8_t
, когда присутствует, является 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 также использовал то же предложение в комментарии к этому сообщению.
Имеющаяся у меня рабочая черновая копия N3376 в [cstdint.syn] § 18.4.1 указывает, что типами int обычно являются 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
Поскольку единственное требование состоит в том, что оно должно быть 8 битами, тогда typedef to char является приемлемым.
char
/signed char
/unsigned char
три разных типа, и char
не всегда 8 бит. на большинстве платформ они все 8-битные целые, но в std::ostream определена только версия char >>
для поведения, как scanf("%c", ...)
,