鉴于这个 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;
}
标准是否指定此类型是否可以或将是字符类型?
从 C++0x FDIS (N3290) 的 § 18.4.1 [cstdint.syn] 开始,int8_t
是一个可选的 typedef,指定如下:
namespace std {
typedef signed integer type int8_t; // optional
//...
} // namespace std
§ 3.9.1 [basic.fundamental] 规定:
有五种标准有符号整数类型:“<code>signed char”、“<code>short int”、“<code>int”、“<code>long int”和“<code>long long int”。在此列表中,每种类型提供的存储空间至少与列表中它前面的类型一样多。也可能有实现定义的扩展有符号整数类型。标准和扩展的有符号整数类型统称为有符号整数类型。
...
类型
bool
,char
,char16_t
,char32_t
,wchar_t
以及有符号和无符号整数类型统称为整数类型。整数类型的同义词是整数类型。
§ 3.9.1 还规定:
在任何特定的实现中,普通对象可以采用与 a或 an
char
相同的值;哪一个是实现定义的。signed char
unsigned char
很容易得出结论,提供的对象int8_t
的 typedef 可能采用带符号的值;但是,情况并非如此,因为不在有符号整数类型(标准和可能扩展的有符号整数类型)列表中。另见Stephan T. Lavavej对和的评论。char
char
char
std::make_unsigned
std::make_signed
因此,要么int8_t
是 typedef ,要么是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
必须是任何一个signed char
或plain的typedef(如果plain被签名char
,则为后者)。char
如果要将int8_t
值打印为整数而不是字符,可以将其显式转换为int
.
原则上,C++ 编译器可以定义一个 8 位扩展整数类型(可能称为类似的东西__int8
),并int8_t
为其创建一个 typedef。我能想到这样做的唯一原因是避免制作int8_t
字符类型。我不知道有任何 C++ 编译器实际做到了这一点。
int8_t
C99 中引入了这两种整数类型和扩展整数类型。char
对于 C,当类型可用时,没有特殊理由定义 8 位扩展整数类型。
更新:
我对这个结论并不完全满意。int8_t
并uint8_t
在 C99 中引入。在 C 中,它们是否是字符类型并不重要。没有任何操作可以使区别产生真正的不同。(即使putc()
是标准 C 中最低级别的字符输出例程,也将要打印的字符作为int
参数)。int8_t
, and uint8_t
, 如果它们被定义,几乎肯定会被定义为字符类型——但字符类型只是小整数类型。
C++ 提供了operator<<
for char
、signed char
和, 的特定重载版本unsigned char
,因此会std::cout << 'A'
产生std::cout << 65
非常不同的输出。后来,C++ 采用了int8_t
and 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
最流行的平台上(Linux 上的 GCC/Intel/Clang 和 Windows 上的 Visual Studio),但在其他平台上可能是别的东西。
长答案如下。
C++11 标准的第 18.4.1 节提供了概要,<cstdint>
其中包括以下内容
typedef
有符号整数类型int8_t; //optional
稍后在同一节第 2 段中,它说
头文件 [ ] 定义了与C 标准
<cstdint>
中的 7.18 相同的所有函数、类型和宏。
其中 C 标准表示按照 1.1/2 的 C99:
C++ 是一种基于ISO/IEC 9899:1999 编程语言 — C(以下简称C 标准)中描述的 C 编程语言的通用编程语言。
因此, 的定义int8_t
可在 C99 标准的第 7.18 节中找到。更准确地说,C99 的第 7.18.1.1 节说
该
typedef
名称intN_t
指定了一个有符号整数类型,其宽度N
、无填充位和二进制补码表示。因此,int8_t 表示宽度正好为 8 位的有符号整数类型。
此外,C99 的第 6.2.5/4 节说
有五种标准有符号整数类型,指定为signed 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
.
鉴于这个 C++11 程序,我应该期望看到一个数字还是一个字母?还是不抱期望?
简短回答:在最流行的平台(Linux 上的 GCC/Intel/Clang 和 Windows 上的 Visual Studio)中,您肯定会看到字母“A”。在其他平台上,您可能会看到65
。(感谢DyP向我指出这一点。)
在续集中,所有引用均指向 C++11 标准(当前草案,N3485)。
第 27.4.1 节提供了 的概要<iostream>
,特别是它声明了cout
:
extern ostream cout;
现在,ostream
是根据第 27.7.1 节typedef
的模板专业化:basic_ostream
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
for的评论char
。
如前所述,C99 标准(上面引用的第 6.2.5/4 节)定义了 5 种标准有符号整数类型(char
不是其中之一),并允许实现添加它们的 onw,它们被称为非标准有符号整数类型。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
可以采用与 a 相同的值signed char
。特别是,char
可能有一个标志,但它仍然不是signed char
. 这在第 3.9.1/1 节中明确说明:
[...] Plain
char
,signed char
和unsigned char
是三种不同的类型。[...] 在任何特定的实现中,普通对象可以采用与 a或 anchar
相同的值;哪一个是实现定义的。signed char
unsigned char
这也意味着它char
不是3.9.1 /2 定义的有符号整数类型。
3 . 我承认我的解释,特别是“char
既不是有符号整数类型也不是无符号整数类型”这句话有点争议。
为了加强我的观点,我想补充一点,Stephan T. Lavavej 在这里说了同样的话,而Johannes Schaub - litb在这篇文章的评论中也使用了同样的句子。
我拥有的工作草案副本 N3376 在 [cstdint.syn] § 18.4.1 中指定 int 类型通常是 typedef。
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 是可以接受的。
char
//是三种不同的类型,asigned char
并不总是8位。在大多数平台上,它们都是 8 位整数,但 std::ostream 仅定义 char 版本的行为,如.unsigned char
char
>>
scanf("%c", ...)