实现可以指定未定义的行为

时间:2015-05-12 11:50:19

标签: c standards language-lawyer undefined-behavior c11

  

3.4.1   
1实施定义的行为
  每个实现都记录了如何做出选择的未指定行为

实现是否可以指定,在未定义的行为可能产生结果的情况下,实现定义的行为是未定义的行为?

例如:

  

6.3.1.3有符号和无符号整数
  3否则,新类型已签名且值无法在其中表示; 要么是   结果是实现定义或引发实现定义的信号。

因此,只要记录在案,这个结果是否可以由实现未定义并导致未定义的行为,或者必须为该实现具有定义的结果?

4 个答案:

答案 0 :(得分:2)

您可以在 C Rationale 中找到“实施定义”意图的更详细说明 - 而不是标准文档本身,但是一个很好的参考

在第1.6章Definition of terms中:

  

实现定义的行为为实现者提供了选择适当方法的自由,但要求向用户解释此选择。指定为实现定义的行为通常是用户可以基于实现定义做出有意义的编码决策的行为。在决定实施定义应该有多广泛时,实施者应该牢记这个标准。与未指定的行为一样,只是无法转换包含实现定义的行为的源不是一个充分的响应。

您无法根据未定义的行为做出“合理的编码决定”。

C FAQ(再次,不是标准,但是众所周知的参考)是quite clear too

  

实现定义:实现必须选择一些行为;它可能无法编译该程序。 (使用构造的程序不正确。)必须记录选择。标准可以指定一组允许的行为,可以从中选择,也可以不强制要求

未指定和实现定义的行为都不是错误 - 编译器不能使转换失败。它们旨在提供实现选项,以便为目标环境生成最佳代码。

答案 1 :(得分:2)

不,语义上这是不可能的。未定义的行为是术语陈述的行为,标准未定义的行为。如果标准请求实现定义的行为,它会显式请求实现指定在发生特定错误时它执行的操作。

  

未定义的行为

     

行为,使用不可移植或错误的程序构造或   错误的数据,本国际标准没有规定   要求

所以一个实现可以说“在那个和那个情况下这个实现引发了一个模糊信号”但它不能说“在那个情况下我们不告诉你什么我们正在做“

不要将未定义的行为误认为会发生的事情,甚至是可以定义的事物。

答案 2 :(得分:1)

The assignment of integers to smaller types is something of an oddball, since the standard clearly recognizes that some implementations may trap, but--uniquely--it requires that the trapping abide by the rules of signals; the decision to impose that requirement here but not elsewhere is somewhat curious, since in many cases it would impede what would otherwise be a straightforward optimization--replacing a signed integer variable whose type shorter than getLeft, and whose address is never taken, with an int.

Nonetheless, for whatever reasons, the authors of the standard have gone out of their way to forbid that optimization. [Note: If I were in charge of the standard, I would specify that an explicit cast to a shorter integer type would yield a value which when cast to an unsigned type of the same size would yield the same result as casting the value directly whenever such a value exists, but that a store of an oversized value directly to an lvalue without a cast would not be thus constrained; I didn't write the standard, though].

It's ironic, actually: given:

int

On a platform where uint64t signedpow(int32_t n, uint32_t p) { uint64_t result; while(p--) { n*=n; result+=n; } return result; } uint64t unsignedpow(uint32_t n, uint32_t p) { uint64_t result; while(p--) { n*=n; result+=n; } return result; } is 32 bits, the latter would have defined semantics for all values of int and n, while the former would not, but on a platform where p is 64 bits, the reverse would be true. A compiler for a typical 64-bit platform which didn't want to waste code on some other defined behavior would be required by the standard to mask off and sign-extended the signed int after each multiplication, but with some unsigned values the compiler could do anything it wanted, including going back in time and pretending that no implementation would ever promise to always perform half-sized unsigned multiplies in a fashion consistent with modular arithmetic.

答案 3 :(得分:0)

根据C11标准,第3.4.1章,

  

实现定义的行为

     

未指明的行为,其中每个实现记录了如何做出选择

因此,每个实现都必须做出选择。否则,它不会符合。因此,我们可以说,必须具有该实现的定义结果。这可以是以下任何一个

  • 定义的行为,特定于该实现,如果遇到该指令,将执行该行为。
  • 一个定义的信号,如果遇到这种情况,它将被劫持。 (大多数情况下,说不能处理。)

相关:

  1. 来自C99 Rationale Document,第3章,(强调我的
  2.   

    实现定义的行为使实现者可以自由选择适当的方法,但需要向用户解释此选择。指定为实现定义的行为通常是 用户可以根据实现的定义 做出有意义的编码决策的行为。在决定实施定义应该有多广泛时,实施者应该牢记这个标准。与未指定的行为一样,只是无法转换包含实现定义行为的源不是一个充分的响应。

    现在,没有人可以采取有意义的编码决策"基于未定义的行为。

    1. 来自C FAQ,问题11.33,
    2.   

      实现定义:实现必须选择一些行为;它可能无法编译该程序。 (使用该构造的程序不正确。)必须记录选择。标准可以指定一组允许的行为,可以选择,也可以不强制要求。

      第一句包含必须,与我在回答中提到的相同。