Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 什么时候未定义的行为可以被认为是众所周知和被接受的?_C_Windows_Undefined Behavior - Fatal编程技术网

C 什么时候未定义的行为可以被认为是众所周知和被接受的?

C 什么时候未定义的行为可以被认为是众所周知和被接受的?,c,windows,undefined-behavior,C,Windows,Undefined Behavior,我们知道什么是未定义的行为,我们(或多或少)知道其中大多数行为的原因(性能、跨平台兼容性)。假设给定的平台,例如Windows 32位,我们可以考虑一个未定义的行为作为众所周知的和一致的跨平台?我理解没有一个通用的答案那么我将限制为两个常见的UB我在生产代码中经常看到(多年来一直在使用) 1) 。给这个联合体: union { int value; unsigned char bytes[sizeof(int)]; } test; 初始化如下: test.value = 0x1

我们知道什么是未定义的行为,我们(或多或少)知道其中大多数行为的原因(性能、跨平台兼容性)。假设给定的平台,例如Windows 32位,我们可以考虑一个未定义的行为作为众所周知的和一致的跨平台?我理解没有一个通用的答案那么我将限制为两个常见的UB我在生产代码中经常看到(多年来一直在使用)

1) 。给这个
联合体

union {
    int value;
    unsigned char bytes[sizeof(int)];
} test;
初始化如下:

test.value = 0x12345678;
然后通过以下方式访问:

for (int i=0; i < sizeof(test.bytes); ++i)
    printf("%d\n", test.bytes[i]);
for(int i=0;i
2) 。给定一个无符号
short*
数组,将其强制转换为(例如)
float*
并访问它(,数组成员之间没有填充)

代码是否依赖于众所周知的UBs(如那些)按案例运行(假设编译器可能会更改,并且编译器版本肯定会更改),或者即使它们是跨平台代码的UB,它们也依赖于特定于平台的详细信息(如果我们不更改平台,则不会更改)?同样的推理是否也适用于未指定的行为(当编译器文档没有说明它时)


编辑根据从C99开始,类型双关只是未指定,而不是未定义。

未定义的行为主要是指一件非常简单的事情,所讨论的代码的行为没有定义,因此C标准没有提供任何可能发生什么的线索。不要在其中搜索更多内容

如果C标准没有定义一些东西,那么您的平台可以作为扩展来定义。因此,如果您遇到这种情况,您可以在该平台上使用它。但是,请确保他们记录了该扩展,并且在下一版本的编译器中不会对其进行更改


由于几个原因,你的例子有缺陷。正如注释中所讨论的,
union
s用于类型双关,尤其是对内存的访问,因为任何字符类型都是允许的。你的第二个例子真的很糟糕,因为除了你似乎暗示的以外,这在我所知道的任何平台上都是不可接受的演员阵容
short
float
通常具有不同的对齐属性,使用这样的东西几乎肯定会使程序崩溃。然后,第三,你在争论Windows上的C,这是因为它们不遵循C标准。

首先,任何编译器实现都可以在任何情况下自由定义它喜欢的任何行为,从标准的角度来看,这些行为会产生未定义的行为

其次,为特定编译器实现编写的代码可以自由使用该实现记录的任何行为;但是,这样做的代码可能无法在其他实现上使用

C语言的一个长期存在的缺点是,尽管在许多情况下,某些实现中可能存在未定义行为的构造被其他实现有效地处理,只有极少数这种情况提供了代码可以指定不以某种方式处理它们的编译器应该拒绝编译的方法。此外,在许多情况下,标准委员会允许完全使用UB,即使在大多数实现中,“自然”结果会受到更多限制。考虑,例如(假设代码> int /代码>为32位)

如果16位编译器使用16位对
h1
h2
执行数学运算,则16位编译器可能最快;如果将
l2
减去
w2
的64位结果添加到
w1
中,则64位编译器可能最快,但如果代码是为32位系统编写的,能够让其他两个系统的编译器生成与32位系统相同的代码比让它们生成执行不同计算的代码更有用,无论后一个代码的速度有多快


不幸的是,目前还没有任何标准方法可以让代码要求这样的语义[这一事实在许多情况下可能会限制64位代码的效率];最好的办法可能是在某个地方明确记录代码的环境要求,并希望使用代码的人能够看到这些要求。

如果我没记错的话,以这种方式使用联合实际上是定义良好的,并在规范中提到过。旁注:将
4
更改为
sizeof(int)
,这在每个平台上都不一定是4。@JoachimPileborg,C90定义了它的实现。C99有点模糊,但目的是允许类型双关(参见)。@barakmanos你说得对!在C11标准中,其脚注95(在§6.5.2.3中):“如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为6.2.6中所述的新类型的对象表示(一个有时被称为“类型双关”的过程。)“1)好的,带有union的类型双关是(从C99开始)。顺便说一句,联盟不是为了共享内存而建立的吗?类型双关语在-UB之后出现了20多年。。。2) 是一个短填充元素数组,而不仅仅是在末尾?3) 就是这样,但这是一个平台,我会限制这个问题(主要是因为它运行在更小种类的硬件架构上)。1)为什么你认为这是未指定的?该标准明确允许访问
联合体的不同字段。类型双关是从C一开始就存在的,特别是在Unix中,C就是为Unix设计的。2) 数组从不填充。3) 我不明白你的句子。1)来自链接帖子。它就在那里,但可以肯定的是,在C99之前它是UB(它一直在工作?也许……这就是我在这里要问的!)2)对……那么对齐就不是iss了
int weird(uint16_t x, int64_t y, int64_t z)
{
  int r=0;
  if (y > 0) return 1;
  if (z < 0x80000000L) return 2;
  if (x > 50000) r |= 31;
  if (x*x > z) r |= 8;
  if (x*x < y) r |= 16;
  return r;
}
uint64_t l1,l2; uint32_t w1,w2; uint16_t h1,h2;
...
l1+=(h1+h2);
l2+=(w2-w1);