61

鉴于这个 C++11 程序,我应该期望看到一个数字还是一个字母?还是不抱期望?

#include <cstdint>
#include <iostream>

int main()
{
    int8_t i = 65;
    std::cout << i;
}

标准是否指定此类型是否可以或将是字符类型?

4

5 回答 5

27

从 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或 anchar相同的值;哪一个是实现定义的。signed charunsigned char

很容易得出结论,提供的对象int8_t的 typedef 可能采用带符号的值;但是,情况并非如此,因为不在有符号整数类型(标准和可能扩展的有符号整数类型)列表中。另见Stephan T. Lavavej对和的评论。charcharcharstd::make_unsignedstd::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] 来确定:

  • 如果是精确匹配(即 typedef 的),template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char)则将调用该模板。int8_tsigned charsigned char
  • 否则,int8_t将提升为intbasic_ostream<charT,traits>& operator<<(int n)调用成员函数。

在对象的情况std::cout << u下:uuint8_t

  • 如果template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char)模板uint8_tunsigned char.
  • 否则,由于int可以表示所有uint8_t值,因此uint8_t将提升为intbasic_ostream<charT,traits>& operator<<(int n)调用成员函数。

如果你总是想打印一个字符,最安全、最清晰的选择是:

std::cout << static_cast<signed char>(i);

如果你总是想打印一个数字:

std::cout << static_cast<int>(i);
于 2013-04-14T18:33:00.697 回答
23

int8_t正好是 8 位宽(如果存在)。

唯一可以为 8 位的预定义整数类型是charunsigned charsigned char。两者shortunsigned short都要求至少为 16 位。

所以int8_t必须是任何一个signed char或plain的typedef(如果plain被签名char,则为后者)。char

如果要将int8_t值打印为整数而不是字符,可以将其显式转换为int.

原则上,C++ 编译器可以定义一个 8 位扩展整数类型(可能称为类似的东西__int8),并int8_t为其创建一个 typedef。我能想到这样做的唯一原因是避免制作int8_t字符类型。我不知道有任何 C++ 编译器实际做到了这一点。

int8_tC99 中引入了这两种整数类型和扩展整数类型。char对于 C,当类型可用时,没有特殊理由定义 8 位扩展整数类型。

更新

我对这个结论并不完全满意。int8_tuint8_t在 C99 中引入。在 C 中,它们是否是字符类型并不重要。没有任何操作可以使区别产生真正的不同。(即使putc()是标准 C 中最低级别的字符输出例程,也将要打印的字符作为int参数)。int8_t, and uint8_t, 如果它们被定义,几乎肯定会被定义为字符类型——但字符类型只是小整数类型。

C++ 提供了operator<<for charsigned char和, 的特定重载版本unsigned char,因此会std::cout << 'A'产生std::cout << 65非常不同的输出。后来,C++ 采用了int8_tand 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 << ...

于 2013-04-09T20:34:17.650 回答
8

我会按相反的顺序回答你的问题。

标准是否指定此类型是否可以或将是字符类型?

简短的回答int8_tsigned 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 charshort intintlong intlong 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_tsigned char. Intel 和 Clang,至少在 Linux 上,也使用 libc,因此同样适用于它们。因此,在最流行的平台上int8_tsigned 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_tsigned 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成为typedeffor的评论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 charunsigned char三种不同的类型。[...] 在任何特定的实现中,普通对象可以采用与 a或 anchar相同的值;哪一个是实现定义的。signed charunsigned char

这也意味着它char不是3.9.1 /2 定义的有符号整数类型。

3 . 我承认我的解释,特别是“char既不是有符号整数类型也不是无符号整数类型”这句话有点争议。

为了加强我的观点,我想补充一点,Stephan T. Lavavej 在这里说了同样的话,而Johannes Schaub - litb在这篇文章的评论中也使用了同样的句子。

于 2013-04-15T00:49:53.463 回答
5

我拥有的工作草案副本 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 是可以接受的。

于 2013-04-09T20:24:50.750 回答
-1

char//是三种不同的类型,asigned char并不总是8位。在大多数平台上,它们都是 8 位整数,但 std::ostream 仅定义 char 版本的行为,如.unsigned charchar>>scanf("%c", ...)

于 2013-04-14T12:28:30.607 回答