use*_*537 7 c standard segmentation-fault posix null
正如我们所知,C 标准没有指定很多实现的细节,例如 NULL 指针的值、位和字节的顺序(字节序)、结构和堆栈参数的对齐、内存的实际组织(我不不认为堆栈布局是必要的,只是“自动”)。此外,一些其他部分被定义(或未定义)以产生未定义的行为,例如取消引用 null(通常是段错误,但不一定)
由于现代平台(或者由编译器决定?)(故意?)行为类似,因此依赖程序的某些一致行为实际上是危险的。
那么,是否有一个 unix 变体或 unix 启发的平台,(或者一个编译器,如果它主要取决于编译器?)这是标准的,但故意奇怪地产生错误并指出未定义的行为和不可移植性?除了 POSIX 而不是 C 之外,是否存在类似的东西?尝试和实施甚至是合理的吗?
Hewlett Packard 的 HP-UX 是我能想到的最接近的。
C 编译器将允许取消引用 NULL 指针,尽管有一个标志可以改变这一点。在 PA-RISC 硬件上,堆栈“向上”增长(朝向更大的地址),而堆则向下增长(朝向更小的地址)。
除了堆栈/堆反转之外,PA-RISC 硬件也有一些奇怪之处。它有一个段寄存器(与 x86 段的工作方式不同),因此各种库实际上位于不同的段中,并且指向函数的指针不是一个单一的 64 位指针。但是,我不记得 PA-RISC 是否像 SPARC 一样严格地对待指针对齐。
在更可用的级别上,您可以使用 C 编译器,如Clang、Pcc甚至Tcc,尽管 Tcc 由于我理解的“弱符号”而在 GNU 链接器和加载器方面存在一些问题。至少,您会从这些备用编译器处收到不同的警告消息,这总是值得的。
您还可以尝试替代 C 库,例如Diet Libc或Musl。与 GNU Libc 相比,在针对 Musl 编译时,我的代码做了不同的事情。这两个库都支持静态链接,而 GNU Libc 使静态链接变得困难或不可能。Musl 甚至有一个非常不同的动态链接系统,它可能会暴露您代码中的潜在错误。
那么,是否有一个 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 位上也会暴露很多面子——但这显然不是纠正错误的理想方式。
最后:事实上,测试软件的方法是测试它。 你一边写一边写特定的测试,然后一遍又一遍地运行它们:在一天结束时,每当你添加一个新的部分时,等等。 没有什么可以替代的。