溢出的发生方式有何不同?

  • 本文关键字:何不同 方式 溢出 c
  • 更新时间 :
  • 英文 :


我是C的新手,仍然在努力理解溢出是如何发生的。假设我们有以下错误代码来确定一个字符串是否比另一个字符串长:

int strlonger(char *s, char *t) {
return strlen(s) - strlen(t) > 0;  // let's say the first return value of strlen(s) is s1, abd the second is s2
}

而且我们知道它不起作用,因为strlen()的返回类型是size_tunsigned int,所以当我们有像1u - 2u > 0这样的 sth 时;左操作数溢出。

我有点明白了,就像1u - 2u是 -1,但是因为 s1 和 s2 都是unsigned int,结果也应该是unsigned int的,因此它溢出了。

但考虑不同的情况:

int a= 1048577;
size_t b = 4096;
long long unsigned c= a* b;

由于 1048577*4096 = 4294971392超出了 int 或无符号 b 的范围,那么结果不是应该先溢出吗? 为什么结果被保留为保留值只是因为左操作数 clong long unsigned可以保存该值?,让它仅以这种方式工作不是更明智吗:

long long unsigned a= 1048577;
long long unsigned b = 4096;
long long unsigned c= a* b;

我有点明白了,就像 1u - 2u 是 -1,但由于 s1 和 s2 都是无符号 int,结果也应该是无符号 int,因此它会溢出。

一点也不。

当然,结果是您希望它成为的任何类型(对于我所关心的一切,它可以double(,但该结果类型并不重要 - 或者至少它不是最重要的,因为它不会影响操作本身是否"OK"。必须先定义操作本身,然后才能开始考虑将结果转换为任何类型(或将其保留为"自然"类型(。

您应该关注的是是否定义了诸如对相同无符号类型的两个值进行减法之类的运算。事实上,它总是被定义的。C 标准说明了结果是什么 - 很明显没有溢出。事实上,它更清楚:结果永远不会溢出

涉及无符号

操作数的计算永远不会溢出,因为无法由生成的无符号整数类型表示的结果将缩模为大于结果类型可表示的最大值 1 的数字。(ISO/IEC 9899:1999 (E( §6.2.5/9(

不仅如此,整数和无符号整数之间的转换也得到了很好的定义,并且-1(整数类型(转换为您将其转换为的任何无符号类型的最大值。基本上,-1转换为无符号 int 是编写UINT_MAX等的简短方法。

unsigned char uc = -1;
assert(uc == UCHAR_MAX);
unsigned short us = -1;
assert(us == USHORT_MAX);
unsigned int ui = -1;
assert(ui == UINT_MAX);
unsigned long ul = -1;
assert(ul == ULONG_MAX);
// etc.

long long unsigned c= a* b;由于 1048577*4096 = 4294971392超出了 int 或无符号 b 的范围,那么结果不是应该先溢出吗?

C 语言根本不是为了以你的方式解释它而设计的。仅此而已。编程语言设计中的大多数决策都是完全任意的。当然,你可能会惊讶于设计师做出的决定与你不同的决定,但两者都是同样武断的。

这里发生的情况是,整个计算是使用long long unsigned类型执行的,并且因为它是无符号类型,所以它永远不会溢出。C 标准是这样说的。仅此而已。

有人可能会争辩说,按照你提议的方式去做会更糟,因为会有更多的打字来获得一些看起来应该有效的东西。如果 C 按照您想要的方式工作,则需要按如下方式编写表达式:

int a = 1048577;
size_t b = 4096;
long long unsigned c = (long long unsigned)a * (long long unsigned)b;

有人可能会争辩说,强迫每个人以这种方式用无休止的强制转换来污染他们的代码至少可以说是不友善的。C 比你想象的要好。

当然,C也充满了令人憎恶的东西,所以你很幸运,你问了这个,而不是说,关于为什么gets()不好的第一百万个问题。事实是:gets()就像沃尔德莫特一样。你不说gets,你不用gets一切都很好。

相关内容

最新更新