是否有“深奥”(奇怪)但符合标准的 C 编译器或运行时?

use*_*537 7 c standard segmentation-fault posix null

正如我们所知,C 标准没有指定很多实现的细节,例如 NULL 指针的值、位和字节的顺序(字节序)、结构和堆栈参数的对齐、内存的实际组织(我不不认为堆栈布局是必要的,只是“自动”)。此外,一些其他部分被定义(或未定义)以产生未定义的行为,例如取消引用 null(通常是段错误,但不一定)

由于现代平台(或者由编译器决定?)(故意?)行为类似,因此依赖程序的某些一致行为实际上是危险的。

那么,是否有一个 unix 变体或 unix 启发的平台,(或者一个编译器,如果它主要取决于编译器?)这是标准的,但故意奇怪地产生错误并指出未定义的行为和不可移植性?除了 POSIX 而不是 C 之外,是否存在类似的东西?尝试和实施甚至是合理的吗?

Bru*_*ger 7

Hewlett Packard 的 HP-UX 是我能想到的最接近的。

C 编译器将允许取消引用 NULL 指针,尽管有一个标志可以改变这一点。在 PA-RISC 硬件上,堆栈“向上”增长(朝向更大的地址),而堆则向下增长(朝向更小的地址)。

除了堆栈/堆反转之外,PA-RISC 硬件也有一些奇怪之处。它有一个段寄存器(与 x86 段的工作方式不同),因此各种库实际上位于不同的段中,并且指向函数的指针不是一个单一的 64 位指针。但是,我不记得 PA-RISC 是否像 SPARC 一样严格地对待指针对齐。

在更可用的级别上,您可以使用 C 编译器,如ClangPcc甚至Tcc,尽管 Tcc 由于我理解的“弱符号”而在 GNU 链接器和加载器方面存在一些问题。至少,您会从这些备用编译器处收到不同的警告消息,这总是值得的。

您还可以尝试替代 C 库,例如Diet LibcMusl。与 GNU Libc 相比,在针对 Musl 编译时,我的代码做了不同的事情。这两个库都支持静态链接,而 GNU Libc 使静态链接变得困难或不可能。Musl 甚至有一个非常不同的动态链接系统,它可能会暴露您代码中的潜在错误。


gol*_*cks 6

那么,是否有一个 unix 变体或 unix 启发的平台,(或者一个编译器,如果它主要取决于编译器?)这是标准的,但故意奇怪地产生错误并指出未定义的行为和不可移植性?

不,因为...

尝试和实施甚至是合理的吗?

...如果合理意味着“任何人都会合理地想要使用的工具”。但是是的,因为这已经是普通的 C 编译器。它们不可避免地允许您以定义的(可预测的)方式沉迷于未定义的行为。例如:

i = i++ + ++i;
Run Code Online (Sandbox Code Playgroud)

未定义,因为您i在序列点之间进行了多次修改。但是,几乎可以肯定,给定的编译器每次都会产生完全相同的结果。当然,这不会被记录下来,因为您首先不应该这样做,但是编译器可能知道并且可以在您询问时告诉您。

把它放在一个文件中并编译它,gcc undefined.c. 没问题。现在尝试启用一些警告,gcc -Wall -pendantic undefined.c

undefined.c: In function ‘main’:
undefined.c:8:4: warning: operation on ‘i’ may be undefined [-Wsequence-point]
  i = i++ + ++i;
    ^
Run Code Online (Sandbox Code Playgroud)

你去吧。现在,请记住,编译器每次都会在这里产生相同的结果,您可以决定将其用于任何目的。这意味着如果我们将“错误”定义为您不希望发生的事情,那么编译器不可能在您做错事时“故意……产生错误”。它不知道你想发生什么,也不能。因此,它不能故意产生您无法预测的结果。

然而,它显然可以“指出未定义的行为”。

如果您想要一些比编译器更能发现错误的东西,请查看valgrind。在同一条线上还有其他(可能非常非常非常昂贵)的东西,但这是我唯一使用过的。

将你专门在 64 位机器上开发的东西编译并运行在 32 位上也会暴露很多面子——但这显然不是纠正错误的理想方式。

最后:事实上,测试软件的方法是测试它。 你一边写一边写特定的测试然后一遍又一遍地运行它们:在一天结束时,每当你添加一个新的部分时,等等。 没有什么可以替代的。