【问题标题】:Overflow of an unsigned char in C++C++ 中无符号字符的溢出
【发布时间】:2020-06-07 09:39:01
【问题描述】:

我在这里https://www.toptal.com/c-plus-plus/interview-questions 找到了以下代码sn-p,问题是:这个循环将执行多少次?解释你的答案

unsigned char half_limit = 150;

for (unsigned char i = 0; i < 2 * half_limit; ++i)
{
    // do something;
}

答案是无限的。

表达式 2 * half_limit 将被提升为 int(基于 C++ 转换规则),其值为 300。但是,由于我 是一个无符号字符,它由一个 8 位值重新表示,之后 达到 255,将溢出(所以它会回到 0)和循环 因此将永远持续下去。

我猜了 45 次 (300-255),因为我记得如果你增加一个无符号整数计数器,当它溢出时计数器会再次从 0 开始。好的,所以它从整数转换为无符号字符,那为什么不是 255 呢?

谢谢

【问题讨论】:

  • 解释中有什么不明白的地方?如果不重复完全相同的2 * half_limit 被提升为int,很难回答,它的值是300,而i 永远不能是&gt;300,因为它是unsigned char。
  • 答案是未定义的行为,除非您可以证明循环体具有可观察到的效果。没有可观察行为的无限循环是 UB,可以完全优化。
  • 你说的是Okay, so it casts from an integer to an unsigned char, why its not 255 then?,但就在你之前提到The expression 2 * half_limit will get promoted to an int
  • @FrançoisAndrieux 我们不知道// do something; 是否有可观察到的行为。
  • @Ayxan 好吧,这是一条评论,所以我们知道它什么都不做 :) 如果有我们不知道的东西来代替评论,那么答案是:我们不知道知道。

标签: c++ casting overflow


【解决方案1】:

那为什么不是 255 呢?

因为 255 小于 300。在下一次迭代中,i 将是 0,也小于 300。8 位整数表示的数字不能达到 300。


从技术上讲,正确的答案是迭代次数取决于unsigned char 的大小。在使用 16 位字节的系统上,不会有问题。

答案的另一个问题是语言中不允许“无限”循环,除非在循环中采取某些操作 - 例如生成一些输出。所以实际上,示例程序具有未定义的行为(假设为 8 位字节)。引用自 标准:

[intro.progress]

实现可能假设任何线程最终都会做一个 以下内容:

  • 终止,
  • 调用库 I/O 函数,
  • 通过易失性左值执行访问,或
  • 执行同步操作或原子操作。

[ 注意:这是为了允许编译器转换,例如 删除空循环,即使无法证明终止。 - 结尾 注意]

【讨论】:

    【解决方案2】:

    小于int 的类型的算术首先将它们提升为int。因此2 * half_limit 是300。假设unsigned char可以表示的最大值是255,那么i的所有值都可能满足i &lt; 2 * half_limit,因此这是一个无限循环。

    【讨论】:

      【解决方案3】:
      2 * half_limit
      

      等同于:

      int(2) * int(half_limit)
      

      half_limit 被提升为整数进行乘法运算,表达式的结果为 300。因此,

      i < 2 * half_limit
      

      变成

      i < int(300)
      

      i 被提升为 int 以进行实际比较,但由于 i 是无符号字符,它永远不能大于 255(假设我们的无符号字符是 8 位),因此比较有效:

      int(smaller than 256) < int(300)
      

      当然,这总是正确的。

      【讨论】:

        【解决方案4】:
        #include <iostream>
        
        int main(int argc,char* argv[]){
        
            unsigned char half_limit = 150;
        
            for (unsigned char i = 0; i < 2 * half_limit; ++i)
            {
                printf("%.2X\n", i);
            }
        }
        

        使用 mingw g++ 编译此代码。给了我一个无限循环,打印十六进制 i 的值表明它每次溢出时都会重置。也许当它溢出时它会设置一个标志.. 如果我错了,请纠正我

        【讨论】:

          猜你喜欢
          • 2011-04-23
          • 2013-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-29
          • 2016-05-22
          • 1970-01-01
          相关资源
          最近更新 更多