【问题标题】:Is overflow of intN_t well-defined? [duplicate]intN_t 的溢出是否定义明确? [复制]
【发布时间】:2015-06-14 12:29:41
【问题描述】:

在 C99 中,有一些(可选)类型,如 int8_tint16_t 等,它们保证具有精确指定的宽度且没有填充位,并以二进制补码表示数字 (7.18.1.1) .在 6.2.6.2 中,有符号整数溢出在脚注 44) 和 45) 中提到,即它可能导致在填充位中捕获值

由于intN_t 没有任何填充位,并且它们保证是二进制补码,这是否意味着它们的溢出不会产生任何未定义的行为?例如,结果会是什么?溢出乘法?加法呢?结果是否与无符号类型一样以 2^N 为模减少?

【问题讨论】:

  • IANALL 但即使它们用 2 的补码表示,它们仍然不遵守算术模 2^n 的定律,因此溢出的结果在数学上没有定义。标准规定“如果在评估表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。”。
  • 即使对于uintN_t,溢出也不是(通常)明确定义的。 intN_tuintN_t 仍然受到整数提升的影响,并且可以在 uint16_t 提升为 int 的系统上将两个 uint16_t 值相乘,以产生无法在 @987654331 中表示的结果@.
  • 注意:int8_tint16_t 等类型在 C99 中并非完全可选:“这些类型是可选的。但是,如果实现提供宽度为 8、16、32 或64 位,无填充位,并且(对于有符号类型)具有二进制补码表示,它应定义相应的 typedef 名称。” §7.20.1.1 3
  • @hvd:我想知道在加法、乘法、按位或左移运算符的结果被强制转换或强制为更小的无符号类型的情况下,是否会有任何实际困难与int 相比,编译器必须生成代码,其行为就像运算符将其操作数强制转换为该类型并使用该类型执行操作一样,或者定义__STDC_NON_MODULAR_UNSIGNED_SHORT。请注意,几乎所有在 2010 年之前生产的编译器(以及之后生产的许多编译器)都已遵守该规则,并且大多数都可以遵守...
  • ...使用命令行选项。该规则不需要对标准定义的任何行为进行任何更改,并且不太可能与任何编译器扩展的任何已定义行为相矛盾。该规则的唯一效果是更改某些类型的未定义行为,这将导致有用优化为独立于平台的行为。

标签: c int undefined-behavior integer-overflow


【解决方案1】:

脚注不规范。如果脚注指出溢出会导致在填充位中捕获值,那并不是真正的错误,但我可以看出它是如何产生轻微误导的。规范性文本只是说行为是未定义的。在填充位中放置陷阱值是未定义行为的一种可能结果,但不是唯一的结果。

所以不,这并不意味着定义了溢出。涉及intN_t/uintN_t 操作数的操作可能会溢出,并且该溢出会导致未定义的行为。

int8_t i = 127; ++i; 之类的东西没有 UB。 int8_t是积分促销,所以加起来就好像你写了i = (int8_t) ((int) i + 1);一样。加法本身不会溢出,转换回int8_t 会产生实现定义的结果。

uint16_t u = 65535; u *= u; 之类的东西在当前典型实现中具有 UB,其中 int 具有 32 个符号/值位。 uint16_t 也有积分提升,所以乘法就像你写了u = (uint16_t) ((int) u * (int) u); 一样进行。乘法本身溢出,行为未定义。

int64_t i = 9223372036854775807; ++i; 之类的东西几乎在所有实现中都有 UB。加法本身溢出。

【讨论】:

  • 不会将u 提升为unsigned int 而不是int
  • @OliverCharlesworth 仅当int 不能代表所有类型的值时。例如,如果uint16_tunsigned short,并且unsigned shortunsigned int 具有相同的大小和范围。但是如果int 足够大,那么就会使用它。很久以前它是不同的,但据我所知,在第一个 C 标准之前,第一个 C 标准要求促销按照我在这里描述的方式工作,从那以后就再也没有改变过。
  • 我想uint16_t u = 65535;, u *= u --> u *= u*1U; 会消除 UB。我认为*1U 技巧也有助于扩大uintN_t
  • @chux 是的,这行得通。我也推荐了1U我自己的乘法:32 bit unsigned multiply on 64 bit causing undefined behavior? :)
  • 无符号算术环绕溢出。未定义的行为仅适用于签名。
【解决方案2】:

不,它没有很好地定义,因为......它根本没有定义。标准中根本没有任何文本可以为有符号整数的溢出提供语义。

不要把“未定义的行为”这个词过分强调为某种神秘的东西,而是直接理解它的含义。没有行为的定义,因此标准没有指定应该发生什么,任何可移植代码都不应依赖这样的特性。

【讨论】:

  • 标准规定 intXX_t 类型应使用补码表示且无填充位存储,还规定将 int 转换为较小的 int 类型,该类型不能保持其值应该以实现定义的方式运行。我想一个实现可能使用二进制补码,但定义转换的二进制补码减少以外的东西,但这将是非常不寻常的。然而,结果不是 UB,在任何情况下,明确定义的 int 被转换为更小的尺寸。
  • @supercat,问题是关于溢出,而不是关于转换。
  • 在 INT_MAX 为 65535 或更大的系统上给定 int16_t x=182;x*=x; 将使 x 等于 -32412 的事实不会像标准中定义的术语那样“溢出”,而是如果 x 被期望表现为一个数字,那么说它溢出似乎是最自然的。
猜你喜欢
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多