【问题标题】:Do the "<" and ">" operators work correctly when size_t overflow?size_t 溢出时“<”和“>”运算符是否正常工作?
【发布时间】:2015-05-20 16:09:14
【问题描述】:

我正在开发一个更新 20 年前的代码的项目,其中许多问题都与整数溢出有关。我想确保我正确地测试溢出,所以我编写了一个测试程序。它的输出让我大吃一惊。这里是:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

int main (void) {

   size_t largerNum,Num;

   largerNum = 12;
   Num = UINT_MAX;

   printf("largerNum = %u\nNum = %u\nNum + 1 = %u\n", largerNum    , Num, Num + 1);

   largerNum = Num + 1;

   printf("largerNum now = %u\n", largerNum);

   if(largerNum < Num ){
      printf("largerNum overflowed to %u\n", largerNum);
   }
   else {
      printf("largerNum did not overflow: %u\n", largerNum);
   }

   printf("Is (0 < UINT_MAX)?\n");

   (0 < UINT_MAX)?printf("YES\n"):printf("NO\n");

   printf("Is (largerNum < Num)?\n");

   (largerNum < Num)?printf("YES\n"):printf("NO\n");

   return 0;
}

及其输出:

[afischer@susm603 /home/afischer/Fischer_Playground/overflowTest]$ main
largerNum = 12
Num = 4294967295
Num + 1 = 0
largerNum now = 0
largerNum did not overflow: 0
Is (0 < UINT_MAX)?
YES
Is (largerNum < Num)?
NO

我查看了其他一些帖子herehere 并阅读了此paper,但它并没有使输出变得清晰。有人见过这个吗?

编辑:当我从 size_t 更改为 unsigned long 时,它可以正常工作,这不应该做任何事情。

  6 int main (void) {
  7 
  8    unsigned long largerNum,Num;
  9 
 10    largerNum = 12;
 11    Num = UINT_MAX;
 12 
 13    printf("largerNum = %u\nNum = %u\nNum + 1 = %u\n", largerNum    , Num, Num + 1);
 14 
 15    largerNum = Num + 2;
 16 
 17    printf("largerNum now = %u\n", largerNum);
 18 
 19    if(largerNum < Num ){
 20       printf("largerNum overflowed to %u\n", largerNum);
 21    }
 22    else {
 23       printf("largerNum did not overflow: %u\n", largerNum);
 24    }
 25 
 26    printf("Is (0 < UINT_MAX)?\n");
 27 
 28    (0 < UINT_MAX)?printf("YES\n"):printf("NO\n");
 29 
 30    printf("Is (largerNum < Num)?\n");
 31 
 32    (largerNum < Num)?printf("YES\n"):printf("NO\n");
 33 
 34 
 35    printf("largerNum = %u\n", largerNum);
 36    printf("Num = %u\n", Num);
 37 
 38    return 0;
 39 }

输出:

[afischer@susm603 /home/afischer/Fischer_Playground/overflowTest]$ main
largerNum = 12
Num = 4294967295
Num + 1 = 0
largerNum now = 1
largerNum overflowed to 1
Is (0 < UINT_MAX)?
YES
Is (largerNum < Num)?
YES
largerNum = 1
Num = 4294967295

编辑2:

在阅读了一些 cmets 后,我将 'UINT_MAX' 替换为 'ULONG_MAX',并且三元运算符正常运行。然后我将“size_t”更改为“unsigned long”。它仍然可以正常工作。对我来说奇怪的是,在我的机器上,“size_t”、“unsigned int”和“unsigned long”都是相同的字节数,“UINT_MAX”和“ULONG_MAX”是相同的值,但是那个三元运算符尽管一切都一样,但仍然会失败。也许不一样?这扰乱了我对 C 的理解。

对于那些感兴趣的人,工作代码:

  6 int main (void) {
  7    /* Can be size_t or unsigned long */
  8    size_t largerNum,Num;
  9 
 10    largerNum = 12;
 11    Num = ULONG_MAX;
 12 
 13    printf("largerNum = %u\nNum = %u\nNum + 1 = %u\n", largerNum    , Num, Num + 1);
 14 
 15    largerNum = Num + 2;
 16 
 17    printf("largerNum now = %u\n", largerNum);
 18 
 19    if(largerNum < Num ){
 20       printf("largerNum overflowed to %u\n", largerNum);
 21    }
 22    else {
 23       printf("largerNum did not overflow: %u\n", largerNum);
 24    }
 25 
 26    printf("Is (0 < ULONG_MAX)?\n");
 27 
 28    (0 < ULONG_MAX)?printf("YES\n"):printf("NO\n");
 29 
 30    printf("Is (largerNum < Num)?\n");
 31 
 32    (largerNum < Num)?printf("YES\n"):printf("NO\n");
 33 
 34    
 35    printf("largerNum = %u\n", largerNum);
 36    printf("Num = %u\n", Num);
 37    
 38    return 0;
 39 }

输出:

[afischer@susm603 /home/afischer/Fischer_Playground/overflowTest]$ main
largerNum = 12
Num = 4294967295
Num + 1 = 0
largerNum now = 1
largerNum overflowed to 1
Is (0 < ULONG_MAX)?
YES
Is (largerNum < Num)?
YES
largerNum = 1
Num = 4294967295

最终编辑:

看了更多的cmets,发现我的printf()语句有误。谢谢大家的帮助,现在一切都变得更有意义了。 =D

最终代码:

  6 int main (void) {
  7 
  8    unsigned long largerNum,Num;
  9 
 10    largerNum = 12;
 11    Num = ULONG_MAX;
 12 
 13    printf("largerNum = %zu\nNum = %zu\nNum + 1 = %zu\n", larger    Num, Num, Num + 1);
 14 
 15    largerNum = Num + 2;
 16 
 17    printf("largerNum now = %zu\n", largerNum);
 18 
 19    if(largerNum < Num ){
 20       printf("largerNum overflowed to %zu\n", largerNum);
 21    }
 22    else {
 23       printf("largerNum did not overflow: %zu\n", largerNum);
 24    }
 25 
 26    printf("Is (0 < ULONG_MAX)?\n");
 27 
 28    (0 < ULONG_MAX)?printf("YES\n"):printf("NO\n");
 29 
 30    printf("Is (largerNum < Num)?\n");
 31 
 32    (largerNum < Num)?printf("YES\n"):printf("NO\n");
 33 
 34 
 35    printf("largerNum = %zu\n", largerNum);
 36    printf("Num = %zu\n", Num);
 37 
 38    return 0;
 39 }

最终输出:

[afischer@susm603 /home/afischer/Fischer_Playground/overflowTest]$ main
largerNum = 12
Num = 18446744073709551615
Num + 1 = 0
largerNum now = 1
largerNum overflowed to 1
Is (0 < ULONG_MAX)?
YES
Is (largerNum < Num)?
YES
largerNum = 1
Num = 18446744073709551615

【问题讨论】:

  • 究竟哪一部分让你感到惊讶?
  • (0 &lt; UINT_MAX) 中,您的编译器是否认为0int?如果是这样,(0 &lt; 0xFFFFFFFF) 是假的。
  • 我认为 size_t 在您的平台上是 64 位的,但您使用了错误的 printf 格式说明符,因此被截断。由于这也被标记为 C++,因此请使用 cout 进行打印,您的问题可能会消失。
  • @Praetorian 我认为你应该从中建立一个答案,%u 根本不是显示size_t 的正确方式。

标签: c printf overflow operator-keyword size-t


【解决方案1】:

我的猜测是您的平台具有 64 位 size_t,并且您使用错误的格式说明符来打印 size_t,即 undefined behavior,并导致输出误导。

对于print size_ts,在 gcc 和 clang 上使用 %zu,在 MSVC 上使用 %Iu。或者忘记所有这些并使用std::cout 打印结果。

在 VS2015 上使用 %Iu,我在 64 位编译器上得到的输出是

largerNum = 12
Num = 4294967295
Num + 1 = 4294967296
largerNum now = 4294967296
largerNum did not overflow: 4294967296
Is (0 < UINT_MAX)?
YES
Is (largerNum < Num)?
NO

【讨论】:

  • 我现在要去查找%zu。我更困惑为什么将 size_t 更改为 unsigned long 会使我的代码正常工作。
  • unsigned long 的处理方式与size_t 的处理方式有何不同?不争辩它被区别对待,我猜只是寻找历史课或对 printf 内部工作原理的解释。
  • @Makenbaccon unsigned long 的范围可能与 size_t 不同。相同的大小并不意味着相同的范围 - 尽管 那个 很少见。
  • 忽略我所有的无知。我已经用那些讨厌的%zu 条目和huzzah 更新了最终代码!一切都说得通。感谢您的时间。 =)
  • @Makenbaccon 尝试打印unsigned intunsigned longsize_t 的大小(记得使用%zu 打印sizeof 的结果:))。前两个大概是 4 个字节,最后一个是 8 个。
【解决方案2】:

只是添加到@Praetorian 的答案并显示类型安全的实现:

#include <iostream>
#include <limits>

int main (void) {
   using std::size_t;
   using std::cout;

   size_t largerNum = 12;
   size_t Num = std::numeric_limits<size_t>::max();

   cout << "largerNum = " << largerNum << "\nNum = " << Num << "\nNum + 1 = " << Num + 1 << "\n";
   largerNum = Num + 1;
   cout << "largerNum now = " << largerNum << "\n";

   if(largerNum < Num ){
       cout << "largerNum overflowed to " << largerNum << "\n";
   }
   else {
       cout << "largerNum did not overflow: " << largerNum << "\n";
   }

   cout << "Is (0 < Unsigned Maximum)?\n";

   (0 < std::numeric_limits<size_t>::max())?cout << "YES\n":cout << "NO\n";

   cout << "Is (largerNum < Num)?\n";

   (largerNum < Num)?cout << "YES\n":cout << "NO\n";

   return 0;
}

因此:'printf' 在 C++ 中不是一个好的选择,它不是类型安全的(尽管好的编译器可以识别无效的格式说明符)。另一方面,iostream 运算符很麻烦(对许多人来说),并且如果必须将输出翻译成不同的语言(例如:gnu getline),则很糟糕。您可以在网上浏览类型安全的格式字符串(例如:boost::format)

【讨论】:

  • 谢谢,这会影响我在 C++ 中的编码方式。我最初标记了这个 c++,但这是一个错误。我将不得不研究 boost::format。再次感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
相关资源
最近更新 更多