Предназначены ли 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 a signed 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", ...),

Другие вопросы по тегам