【问题标题】:Are int8_t and uint8_t intended to be char types?int8_t 和 uint8_t 是 char 类型吗?
【发布时间】:2013-04-01 11:02:29
【问题描述】:

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

#include <cstdint>
#include <iostream>

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

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

【问题讨论】:

  • int 根据规范必须至少 16 位
  • uint8_t 是整数类型,不是字符类型。我期待数字,而不是字母。它看起来像另一个 C++ 委员会失礼(GCC 6.3.1-1 将它们打印为字符)。委员会通过std::byte 部分正确。 std::byte 不作为字符类型打印(目前,它根本不打印。希望将来会修复)。

标签: c++ c++11 iostream language-lawyer standard-library


【解决方案1】:

根据 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] 规定:

有五种标准有符号整数类型:“signed char”、“short int”、“int”、“long int”和“long long int”。在此列表中,每种类型提供的存储空间至少与列表中它前面的类型一样多。也可能存在实现定义的扩展有符号整数类型。标准和扩展的有符号整数类型统称为有符号整数类型

...

类型boolcharchar16_tchar32_twchar_t,以及有符号和无符号整数类型统称为整数类型。整数类型的同义词是整数类型

§ 3.9.1 还规定:

在任何特定实现中,普通的char 对象可以采用与signed charunsigned char 相同的值;哪一个是实现定义的。

如果char 对象采用有符号值,很容易得出结论int8_t 可能是char 的typedef;但是,情况并非如此,因为char 不在有符号整数类型(标准和可能扩展的有符号整数类型)列表中。另请参阅 Stephan T. Lavavej's comments 上的 std::make_unsignedstd::make_signed

因此,int8_t 要么是 signed char 的 typedef,要么是扩展的有符号整数类型,其对象恰好占用 8 位存储空间。

不过,要回答您的问题,您不应该做出假设。因为x.operator&lt;&lt;(y)operator&lt;&lt;(x,y) 两种形式的函数都已定义,所以§ 13.5.3 [over.binary] 说我们参考§ 13.3.1.2 [over.match.oper] 来确定@​​987654350@ 的解释。 § 13.3.1.2 反过来说,实现根据 § 13.3.2 和 § 13.3.3 从候选函数集中进行选择。然后我们查看 § 13.3.3.2 [over.ics.rank] 来确定:

  • 如果int8_tsigned char 完全匹配(即signed char 的类型定义),则将调用template&lt;class traits&gt; basic_ostream&lt;char,traits&gt;&amp; operator&lt;&lt;(basic_ostream&lt;char,traits&gt;&amp;, signed char) 模板。
  • 否则,int8_t 将被提升为 int 并调用 basic_ostream&lt;charT,traits&gt;&amp; operator&lt;&lt;(int n) 成员函数。

std::cout &lt;&lt; u 的情况下,u 是一个uint8_t 对象:

  • 如果uint8_tunsigned char 完全匹配,则将调用template&lt;class traits&gt; basic_ostream&lt;char,traits&gt;&amp; operator&lt;&lt;(basic_ostream&lt;char,traits&gt;&amp;, unsigned char) 模板。
  • 否则,由于int 可以代表所有uint8_t 值,uint8_t 将被提升为int 并调用basic_ostream&lt;charT,traits&gt;&amp; operator&lt;&lt;(int n) 成员函数。

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

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

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

std::cout << static_cast<int>(i);

【讨论】:

  • “标准允许typedef char int8_t”:我相信这不是真的,因为char 是一个整数类型,但它不是有符号整数类型,即使它有符号。关于这个(相当混乱的)术语的(希望是正确的)解释,请参阅我的帖子。
  • @CassioNeri:C++ 标准不能在有符号整数类型无符号整数类型列表中包含char,因为标准允许@987654374 @ 对象采用有符号或无符号值。所以,我不同意你的观点,因为char 没有列在有符号整数类型 列表中,这意味着char 不是有符号整数类型 即使它采用有符号值,因为标准不能有符号整数类型无符号整数类型列表中包含char i>.
  • 虽然你的推理对我来说很有道理,但我仍然相信我所说的。显然 Stephan T. Lavavej agrees with me:“虽然“char”需要与“signed char”或“unsigned char”(这是实现定义的)具有相同的符号和范围,但“char”既不是有符号的整数类型也不是无符号整数类型”。另见Johannes Schaub - litb的评论here
  • @CassioNeri:我现在认为你是对的。感谢您找到这两个论点。由于 Stephan T. Lavavej 写的所有内容对我来说都是有意义的,我认为 std::make_signed&lt;int8_t&gt;::type 必须与 int8_t 相同,因为 int8_t 被指定为 有符号整数类型。因此,int8_t 不能是 chartypedef,即使 char 对象采用有符号值。
【解决方案2】:

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

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

因此,int8_t 必须是 signed char 或普通 char 的 typedef(如果已签名普通 char,则为后者)。

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

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

int8_t 和扩展整数类型都是在 C99 中引入的。对于 C,当char 类型可用时,没有特别的理由定义 8 位扩展整数类型。

更新

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

C++ 为charsigned charunsigned char 提供了特定的operator&lt;&lt; 重载版本,以便std::cout &lt;&lt; 'A'std::cout &lt;&lt; 65 产生非常不同的输出。后来,C++ 采用了int8_tuint8_t,但是就像在C 中一样,它们几乎肯定是字符类型。对于大多数操作,这并不像在 C 中那样重要,但对于 std::cout &lt;&lt; ... 来说确实有所不同,因为:

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 &lt;&lt; ... 的相当奇怪的行为所带来的不便。

【讨论】:

  • 我试图找到一个最小尺寸为short 的参考(至少signed char 的尺寸除外),但我找不到它 - 你能提供参考吗?跨度>
  • C++ 标准 3.9.1:“有符号和无符号整数类型应满足 C 标准第 5.2.4.2.1 节中给出的约束”。 C 5.2.4.2.1 对&lt;limits.h&gt; 设置要求,包括SHRT_MIN &lt;= -32767SHRT_MAX &gt;= +32767USHRT_MAX &gt;= 65535
  • 请记住,实现可以typedefint8_t 到非标准实现定义的类型(并且可能在使用16 位char 的少数平台上很好)。我认为 C++11 标准缺少一些关于这些 stdint.h 类型应如何在重载中解析的必要说明。我怀疑这些类型如何匹配重载解析将由实现定义。
  • @MichaelBurr:如果char 是16 位,那么CHAR_BIT==16,一个字节定义为16 位。除了位域,你不能有小于 1 字节的整数类型。所以在那种情况下不会有int8_t。 (如果你不相信,想想sizeof (int8_t)。)
  • @BenVoigt [over.ics.rank]/4:“标准转换序列按其等级排序:精确匹配是比提升更好的转换,它是比转换更好的转换。 "在这种情况下,提升将是 [conv.prom]/1,即提升到 (unsigned) int(从具有较低转换等级的类型)。转换将是 [conv.integral]/1,即转换为任何整数类型(包括char)。只有char == uint8_t,最可行的功能应该是operator&lt;&lt; (char)AFAIK,否则operator&lt;&lt; (int)
【解决方案3】:

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

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

简答int8_t 在最流行的平台(Linux 上的 GCC/Intel/Clang 和 Windows 上的 Visual Studio)中是 signed char,但在其他平台上可能是别的东西。

下面是长答案。

C++11 标准的第 18.4.1 节提供了&lt;cstdint&gt; 的概要,其中包括以下内容

typedef 有符号整数类型 int8_t; //optional

稍后在同一部分的第 2 段中,它说

标头 [&lt;cstdint&gt;] 定义了与 C 标准中的 7.18 相同的所有函数、类型和宏。

根据 1.1/2,C 标准表示 C99:

C++ 是一种基于ISO/IEC 9899:1999 编程语言——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 intint、long int 和 long long int。 (如 6.7.2 中所述,这些和其他类型可以用几种其他方式指定。)也可能有实现定义的扩展有符号整数类型标准和扩展有符号整数类型统称为有符号整数类型

最后,C99 的第 5.2.4.2.1 节规定了标准有符号整数类型的最小大小。除了signed char,其他所有的长度至少为 16 位。

因此,int8_tsigned char 或 8 位长扩展(非标准)有符号整数类型。

glibc(GNU C 库)和 Visual Studio C 库都将int8_t 定义为signed 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 节提供了&lt;iostream&gt; 的概要,特别是声明了cout

extern ostream cout;

现在,ostreamtypedef,根据第 27.7.1 节,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&lt;&lt;() 的重载,但由于促销和转换,提供的重载之一可能会接受调用。实际上,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

将被调用。这意味着,这段代码

int8_t i = 65;
std::cout << i;

将打印65

更新:

1。更正了DyP的评论后的帖子。

2。添加了以下关于int8_t 成为typedef 的可能性char 的cmets。

如前所述,C99 标准(上面引用的第 6.2.5/4 节)定义了 5 种标准有符号整数类型(char 不是其中之一)并允许实现添加它们的 onw,它们被称为非标准有符号整数类型。 C++ 标准加强了第 3.9.1/2 节中的定义:

有五种标准有符号整数类型:“signed char”、“short int”、“int”、“long int”和“long long int” [...]整数类型。标准和扩展的有符号整数类型统称为有符号整数类型

稍后,在同一节中,第 7 段说:

类型boolcharchar16_tchar32_twchar_t以及有符号和无符号整数类型统称为整数类型 .整数类型的同义词是整数类型

因此,char 是整数类型,但 char 既不是有符号整数类型也不是无符号整数类型,第 18.4.1 节(上面引用)说int8_t,当present,是有符号整数类型的 typedef

可能令人困惑的是,根据实现的不同,char 可以采用与 signed char 相同的值。特别是,char 可能有一个标志,但它仍然不是signed char。这在第 3.9.1/1 节中有明确说明:

[...] 普通 charsigned charunsigned char三种不同的类型。 [...] 在任何特定的实现中,一个普通的char 对象可以采用与signed charunsigned char 相同的值;哪一个是实现定义的。

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

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

为了证明我的观点,我想补充一点,Stephan T. Lavavej 说了同样的话hereJohannes Schaub - litbthis 帖子的评论中也使用了相同的句子。

【讨论】:

  • 我认为int8_t != signed char 不会编译失败,原因如下:1) int8_t 可能是char(不同于signed char 的不同类型) . 2) 即使int8_t 是扩展整数类型,它也将是整数类型,参见[basic.fundamental]/2+7。正如 [conv.prom]/1 告诉我们的,它可以提升为 intunsigned int(因为 int 必须 >= char >= 8 位)。另请参阅丹尼尔的回答。
  • @DyP:你是对的。由于完整的促销/转换,operator&lt;&lt; 的过载将可以接听电话。感谢您指出了这一点。我会更正帖子。但是,据我了解,int8_t 不能是char。我将在这一点上添加更多信息。请让我知道你的想法。
  • char 的状态对我来说并不完全清楚。它是一个整数类型,但既不是有符号也不是无符号整数类型。可以是扩展整数类型的 typedef 吗?
  • 我已经和 Daniel 一起解决了这个问题:[over.ics.rank] 说,在计算最佳可行方案时,积分提升 [conv.prom] 将优于积分转换 [conv.integral]函数(重载)。并且int8_tint 的整体提升当然是可能的(int >= 16 位); uint8_tunsigned int 相同。因此,如果必须对其进行转换,它将被提升为int,并且输出将是65(或任何数字)而不是A。另外,我仍然不确定typedef extended_int char; typedef extended_int int8_t; 是否合法。
  • "标准类型和扩展类型必须不同" 您能否提供参考?我会很感激的。 uint8_t 不能升级signed char,只能升级为intunsigned int 4.5[conv.prom]/1;但由于 C 指定 int >= 16 位,它只能提升为 int。不过,它可以转换signed char,但在重载解决过程中优先提升[over.ics.rank]。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    char/signed char/unsigned char 是三种不同的类型,char 并不总是 8 位。在大多数平台上,它们都是 8 位整数,但 std::ostream 仅为 scanf("%c", ...) 等行为定义了 &gt;&gt; 的字符版本。

    【讨论】:

    • 在定义int8_t的每个平台上它们正好是8位。
    • @BenVoigt 不完全是, 中的CHAR_BIT 定义了char 中有多少位。虽然我没有看到任何 CHAR_BIT 值不是 8 的平台。
    • 如果CHAR_BIT 大于8,则平台上不存在int8_t。标准不允许CHAR_BIT小于8。
    猜你喜欢
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 2017-02-12
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2022-01-23
    相关资源
    最近更新 更多