【问题标题】:Is it safe to implicitly convert a `uint8_t` (read from a socket) to a `char`?将`uint8_t`(从套接字读取)隐式转换为`char`是否安全?
【发布时间】:2020-06-21 17:43:33
【问题描述】:

我对关于无符号到有符号的 C++ 转换规则感到困惑,反之亦然。

我正在从套接字读取数据并将其保存在std::vector<uint8_t> 中。然后我需要阅读其中的一部分 (假设它是 ASCII 数据)并将其保存在 std::string 中。这就是我正在做的:

for (std::vector<uint8_t>::const_iterator it = payload.begin() + start; it < payload.begin() + end; ++it) {
    store_name.push_back(*it);
}

如您所见,*it 返回一个uint8_t 并将其传递给std::stringpush_back 成员函数,该函数接受char - 因此发生了隐式转换。 char 实际上可能是已签名或未签名的。我不确定如果它被签名会发生什么。

我无法理解(没有双关语)这里发生的事情,以及它是否安全。

store_name.push_back(*it) 在将*it 存储到std::string 之前是否会更改其位模式? 究竟有哪些规则来管理这一点?

我已经在网上浏览了很多地方来解释类型转换规则,但它仍然不适合我。解释将不胜感激。

编辑:换一种说法 - 一般来说,当我们将 unsigned 转换为 signed 或反之亦然时会发生什么?

unsigned char a = 50; // Inside the range of signed char
signed char b = (signed char) a;

b 中的位模式是否需要与a 中的位模式相同?或者位模式会改变吗?

还有,相反的方向呢:

a = (unsigned char) b;

再次 - 位模式是否发生变化?还是保证底层位模式保持不变,无论我们进行多少次有符号-无符号转换,只要值在正确的范围内?

如果它是使用(cstyle cast)static_cast&lt;&gt; 的显式转换,还是通过赋值隐式转换,这有关系吗?

【问题讨论】:

  • 我相信 C++20 保证二进制补码,因此行为得到保证。虽然我还没有看到不安全的编译器/架构(在实践中,如果不是标准的话)

标签: c++ type-conversion


【解决方案1】:

来自implicit conversions - Numeric Conversion/Integral conversions

致未签名者

如果目标类型是无符号的,则结果值为 等于源值模 2n 的最小无符号值,其中 n 是用于表示目标类型的位数。那是, 取决于目标类型是更宽还是更窄,有符号 整数被符号扩展[脚注 1] 或被截断和无符号 整数分别被零扩展或截断。

签名人

如果目标类型是有符号的,则值不会改变,如果 源整数可以用目标类型表示。否则 结果是实现定义的(C++20 前)唯一值 目标类型等于源值模 2n 其中 n 是 用于表示目标类型的位数。 (自从 C++20)。 (请注意,这与有符号整数算术不同 溢出,未定义)。

因此,对于范围内的值,不应进行转换。否则,我将其解释为您的机器将值表示为二进制补码,转换为无符号的位(从 C++20 也转换为有符号)和在 C++20 之前定义的实现没有变化。 (我不知道为什么,但我假设大多数编译器不会更改值,即使它们被允许这样做)。


关于 cstyle-caststatic-cast:cstyle-cast 执行 (link)

当遇到 C 风格的强制转换表达式时,编译器 尝试将其解释为以下强制转换表达式,在此 顺序:

a) const_cast(表达式);

b) static_cast(表达式),带有扩展名:指针或 对派生类的引用还允许强制转换为 指向明确基类的指针或引用(反之亦然),甚至 如果基类不可访问(也就是说,此转换忽略 私有继承说明符)。同样适用于将指针转换为 指向明确非虚基成员的指针的成员;

c) static_cast(带扩展)后跟 const_cast; d) reinterpret_cast(表达式);

e) reinterpret_cast 后跟 > const_cast。满足相应强制转换运算符要求的第一个选择被选中,即使它无法编译。

所以对于signed&lt;-&gt;unsiged 转换,cstyle-cast 应该与static_cast 相同。


对于隐式转换 (implicit conversions - Order of the conversions)

隐式转换序列包含以下内容,按此顺序:

  1. 零个或一个标准转换序列;
  2. 零次或一次用户定义的转换;
  3. 零个或一个标准转换序列。

,在哪里

一个标准的转换序列由以下组成,在这个 顺序:

  1. 从以下集合进行零次或一次转换:左值到右值 转换、数组到指针的转换和函数到指针的转换 转换;
  2. 零个或一个数字提升或数字转换;
  3. 零或一函数指针转换; (C++17 起) 4) 零或一 资格调整。

数字转换又是顶部引用的转换。

static_cast 本身使用隐式转换和用户定义转换 (link) 的组合在类型之间进行转换。所以隐式和显式之间应该没有任何区别。

【讨论】:

  • 嗨,Mikael,感谢您的详细回答。所以我试图确保我理解这里的底线,请更正或验证我的理解:将c 从无符号转换为有符号时(例如,uint8_tchar,其中char 已签名),如只要c 的值在signed char 可包含的数字范围内,该值就不会改变。而当转换相反的方向时,仍然如此。并且 - 对于隐式转换(例如,将x 分配给变量)和显式转换(即static_castcstyle_cast)都是这种情况。我说的对吗?
  • @AvivCohn 是的,如果它在范围内,那么两种方式都可以,包括静态、cstyle 和隐式转换。
  • 对于任何数字类型的任何类型的转换(cstyle、static_cast 或隐式)是否都是如此? IE。 - 将T1 a 转换为T2 b 时,如果a 的值可以包含在T2 的数值范围内,b 的值将保持与a 相同?
  • @AvivCohn,您现在是指值还是位表示? floatint 具有不同的表示形式,但可以(静态)相互转换(改变位表示形式)。值应该是相同的如果该值可以在新类型中表示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-23
  • 2011-12-07
  • 2019-10-14
  • 1970-01-01
  • 2017-08-15
  • 2021-07-11
相关资源
最近更新 更多