给定这个C++11程序,我应该期望看到一个数字还是一个字母?或者不要有期望?
#include <cstdint>
#include <iostream>
int main()
{
int8_t i = 65;
std::cout << i;
}
标准是否规定了该类型可以或将成为字符类型?
给定这个C++11程序,我应该期望看到一个数字还是一个字母?或者不要有期望?
#include <cstdint>
#include <iostream>
int main()
{
int8_t i = 65;
std::cout << i;
}
标准是否规定了该类型可以或将成为字符类型?
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
,哪一个是由实现定义的。char
对象采用的是有符号数值,那么int8_t
可能是char
的一个typedef。然而,这并不是情况,因为char
不在signed integer types列表中(标准和可能扩展的有符号整数类型)。另请参见Stephan T. Lavavej's comments关于std::make_unsigned
和std::make_signed
的评论。int8_t
是signed char
的一个typedef,要么它是一个扩展的有符号整数类型,其对象占据完全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] 来确定:
int8_t
是signed char
的精确匹配(即signed char
的typedef),则调用template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char)
模板。int8_t
将提升为int
,并调用basic_ostream<charT,traits>& operator<<(int n)
成员函数。对于u
为uint8_t
对象的std::cout << u
情况:
uint8_t
与unsigned char
完全匹配,则会调用template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, 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);
typedef char int8_t
”:我认为这不是正确的,因为char
是一种整数类型,但即使它有符号,它也不是有符号整数类型。请参阅我的帖子,以获得关于这个(相当令人困惑的)术语的(希望是正确的)解释。 - Cassio Nerichar
列入有符号整数类型或无符号整数类型列表,因为标准允许char
对象采用有符号或无符号值。因此,我不同意您的观点,即仅因为char
未列在有符号整数类型列表中,这意味着char
不是有符号整数类型,即使它采用有符号值,因为标准不能将char
包括在任一有符号整数类型或无符号整数类型列表中。 - Daniel Trebbienstd::make_signed<int8_t>::type
必须与int8_t
完全相同,因为int8_t
被指定为signed integer type。因此,即使char
对象采用有符号值,int8_t
也不能是char
的typedef
。 - Daniel Trebbienint8_t
是一个精确的8位宽度(如果存在)。
可以为8位的预定义整数类型只有char
、unsigned char
和signed char
。而short
和unsigned short
必须至少为16位。
因此,int8_t
必须是signed char
或普通char
(如果普通char
是有符号的)的typedef。
如果要将int8_t
值作为整数而不是字符打印,则可以将其明确转换为int
。
原则上,C++编译器可以定义一个8位的扩展整数类型(可能称为__int8
之类的),并使int8_t
成为其typedef。我能想到的唯一理由是避免使int8_t
成为字符类型。我不知道有哪些C++编译器实际上这样做了。
在C99中引入了int8_t
和扩展整数类型。对于C来说,在可用char
类型的情况下,没有特别的理由定义一个8位的扩展整数类型。
更新:
我对这个结论并不完全满意。在C99中引入了int8_t
和uint8_t
。在C中,它们是否是字符类型并不特别重要;对于没有真正区别的操作,它们都是小整数类型(即使是标准C中最低级别的字符输出例程putc()
也将要打印的字符作为一个int
参数)。如果定义了int8_t
和uint8_t
,它们几乎肯定会被定义为字符类型,但字符类型只是小的整数类型。
C++为char
、signed char
和unsigned char
提供了特定的重载版本的operator<<
,因此std::cout << 'A'
和std::cout << 65
产生非常不同的输出。后来,C++采用了int8_t
和uint8_t
,但以一种几乎肯定是字符类型的方式。对于大多数操作,这与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 << ...
时有些奇怪的行为所带来的不便。
short
类型最小大小的参考资料(除了至少与signed char
类型大小相同),但未能找到 - 请问您能提供一个参考资料吗? - Mark BSHRT_MIN <= -32767
,SHRT_MAX >= +32767
和 USHRT_MAX >= 65535
。 - Keith Thompsonint8_t
typedef
为非标准的实现定义类型(并且在那些使用16位char
的少数平台上很可能如此)。我认为C++11标准在关于这些stdint.h
类型应该如何在重载中解决方面缺少一些必要的澄清。我怀疑这些类型如何匹配以进行重载分辨可能是实现定义的。 - Michael Burrchar
是16位,那么CHAR_BIT==16
,并且字节的定义是16位。除了位域(bit fields)之外,你不能拥有小于1个字节的整数类型。因此,在这种情况下就没有int8_t
。(如果还不确定,请考虑sizeof(int8_t)
。) - Keith Thompson(unsigned) int
。 转换将是[conv.integral] / 1,即转换为任何整数类型(包括char
)。 只有当char == uint8_t
时,最可行的函数应该是operator<< (char)
AFAIK,否则是operator<< (int)
。 - dyp我将按相反的顺序回答你的问题。
标准是否指定此类型可以或将成为字符类型?
简短回答:在大多数流行平台上(Linux上的GCC / Intel / Clang和Windows上的Visual Studio),int8_t
是signed char
,但在其他平台上可能是其他东西。
长答案如下。
C++11标准的第18.4.1节提供了<cstdint>
的概要,其中包括以下内容:
typedef
signed integer typeint8_t; //optional
同一节的后面一段话中,第2段说:
标头[<cstdint>]定义了所有函数、类型和宏,与C标准7.18相同。
其中C标准指的是根据1.1/2确定的C99:
C++是一种通用的编程语言,基于C编程语言描述的ISO/IEC 9899:1999《编程语言-C》(以下简称C标准)。因此,在C99标准的第7.18节中可以找到int8_t的定义。更确切地说,C99的第7.18.1.1节说:“typedef名称intN_t指定带有宽度N的有符号整数类型,没有填充位,并且具有二进制补码表示。因此,int8_t表示带有精确8位宽度的有符号整数类型。”另外,C99的第6.2.5/4节也提到:<iostream>
的概述,特别是它声明了cout
:extern ostream cout;
现在,根据第27.7.1节,ostream
是basic_ostream
的模板特化的typedef
:
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
可能是char
的typedef
,添加了以下注释。
正如上面所说,C99标准(引用了第6.2.5/4节)定义了5种标准有符号整数类型(char
不在其中),并允许实现添加它们自己的非标准有符号整数类型。C++标准在第3.9.1/2节中加强了这个定义:
有五种标准的有符号整数类型:“signed char”、“short int”、“int”、“long int”和“long long 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
中的一个;具体是哪个由实现定义。
这也意味着,根据 3.9.1/2 的定义,char
不是 有符号整数类型。
3. 我承认我的解释和具体地说是“char
不是有符号整数类型也不是无符号整数类型”这句话有点争议。
int8_t != signed char
,它不会编译失败,有以下两个原因:1)int8_t
可以是一个char
(与signed char
不同的独立类型)。2)即使int8_t
是扩展整数类型,它也将是一个整数类型,参见[basic.fundamental]/2+7。正如[conv.prom]/1所告诉我们的那样,它可以被提升为int
或unsigned int
(因为int
必须是> = char
> = 8位)。另请参见Daniel的答案。 - dypchar
的状态并不完全清楚。它是一种 _整数类型_,但既不是 有符号 也不是 _无符号整数类型_。它可能是扩展整数类型的 typedef 吗? - dypint8_t
提升为 int
是完全可能的(int
>= 16 bit);对于 uint8_t
和 unsigned int
也是如此。因此,如果必须进行转换,它将被提升为 int
,输出将是 65
(或任何数字),而不是 A
。此外,我仍然不确定 typedef extended_int char; typedef extended_int int8_t;
是否合法。 - dypuint8_t
不能被“提升”为 signed char
,它只能被提升为 int
或 unsigned int
4.5[conv.prom]/1;但是由于 C 规定 int
>= 16 位,因此它只能被提升为 int
。它可以被“转换”为 signed char
,但在重载决议期间将优先进行提升 [over.ics.rank]。 - dypuint_8
不是 signed char
,那么它可以被提升为 int
,转换为 signed char
并且在重载决议期间提升是更可取的。因此,将打印 65
。我会尽快更新。关于标准和扩展类型不同的问题:这是由 [conv.rank]/1 隐含的。更具体地说,它说:“任何标准整数类型的等级都应大于具有相同大小的任何扩展整数类型的等级。” 如果标准整数类型是扩展整数类型的 typedef,则它们将具有相同的大小和等级。感谢您的精彩讨论。 - Cassio Nerinamespace 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是可接受的。
char
/signed char
/unsigned char
是三种不同的类型,而且 char
并不总是 8 位。在大多数平台上,它们都是 8 位整数,但是 std::ostream 只为像 >>
这样的行为定义了 char 版本,比如 scanf("%c", ...)
。
int8_t
的每个平台上,它们都恰好是8位。 - Ben VoigtCHAR_BIT
定义了一个 char
中有多少位。虽然我还没有见过 CHAR_BIT 值为 8 以外的平台。 - richselianCHAR_BIT
大于8,则平台上不存在int8_t
。标准不允许CHAR_BIT
小于8。 - Ben Voigt
uint8_t
是一种整数类型,而不是字符类型。我期望的是数字,而不是字母。这似乎又是C++委员会的一个错误(GCC 6.3.1-1将它们打印为字符)。委员会在std::byte
方面做得部分正确。std::byte
不会打印为字符类型(目前它根本不打印。希望将来能够修复)。 - jwwuint8_t
是一种整数类型,@jww,毫无疑问。问题只是所有字符类型(以及bool
)也是整数类型,这既有好处也有坏处。(在这种情况下,坏处是编译器不够聪明,无法跟踪类型别名以确定预期的用例。) - Justin Time - Reinstate Monica