Tritwise操作,右旋转和疯作,无法正常运行,并在Malbolge编译器/解释器中抛出分割错误。
在看到关于编码挑战和代码高尔夫的惊人答案后,我决定开始在 Malbolge 编程,并学习用硬编程语言编程。
当我尝试输出固定字符时,我注意到*
和p
(在规范化马尔博尔格中)在我尝试使用它们的大部分时间都抛出分割错误。
我试图使用互联网并查找字符串(在谷歌上)"'马尔博尔格' 疯狂的操作'段故障'"和"'马尔博尔格'向右旋转'段断层'"。我还尝试在不同的上下文中使用这些命令,发现如果没有输入(这不是我想要的),它可以工作。
我正在使用由tio.run或在线尝试托管的在线口译员。
我尝试使用的代码:
归一化马尔博尔格:/*<
马尔博尔格:u&a
在线试用!
归一化马尔博尔格:/p*<v
马尔博尔格:u=%`M
在线试用!
归一化马尔博尔格:/pp<v
马尔博尔格:u=<`M
在线试用!
我预计u&a
、u=%`M
和u=<`M
的输出不会引发任何错误,但实际输出是分段错误。
确切的错误:/srv/wrappers/malbolge: line 3: 21992 Segmentation fault (core dumped) /opt/malbolge/malbolge .code.tio < .input.tio
,其中 21992 可以是任何数字(最有可能在数千到一万中)
我一直在调试器中逐步执行 Malbolge 解释器以诊断这里发生的事情。我会说"恭喜,你在 Malbolge 解释器中发现了一个错误",但考虑到规范和解释器在其他方面不匹配(权威版本通常被视为解释器),这是 Malbolge,据我所知这是预期的行为。(好吧,它可能实际上不是预期的行为,但许多其他功能也不再是被视为重要的编程技术。
Malbolge将所有内容存储在一个大数组中,包括代码和数据。命令的目的是在运行后自行修改(Malbolge术语中的"加密"),但解释器最终并没有完全正确实现:它实际做的是运行一个命令,然后查看代码指针指向的地址并加密它。这就是为什么跳转会在跳转目标之前加密指令,而不是跳转指令本身。
如果您正在运行的命令超出了 33 到 126(包括 33 到 126)的范围,则该命令不会运行(实际上,在我拥有的 Malbolge 解释器版本中,代码和数据指针也没有增加,这似乎不可避免地会导致无限循环;也许还有其他版本可以解决这个问题)。这是一个重要的检查,因为加密例程只是通过索引到查找表来工作;33 到 126 范围之外的值最终将从数组之前或之后读取一些任意字节的内存。
不幸的是,由于代码和数据一起存储在一个大数组中,因此命令最终可能会在运行时自行修改:它在运行之前可能在 33 到 126 范围内(从而导致安全检查成功),但在运行后,它超出了范围,然后加密最终将执行查找表的越界索引。Malbolge 解释器是用 C 语言编写的,对于越界读取具有未定义的行为,但对于距离越界很远的读取,分割错误可能是(但不保证)的行为。
让我们看看代码u&a
会发生什么:
Command A C D memory
start 0 0 0 117, 38, 97, 29432, 98, 29431, 98, 29432, 97, ...
/ input 0 0 117, 38, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt input 1 1 111, 38, 97, 29432, 98, 29431, 98, 29432, 97, ...
* 39378 1 1 111, 39378, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt 39378 2 2 111, ???, 97, 29432, 98, 29431, 98, 29432, 97, ...
如您所见,您实际上并没有旋转加载到A
中的输入;旋转操作从D
指向的地址读取(而不是从A
读取),因此您正在旋转 38(内存位置 1 中*
命令的内存表示形式)以产生 39378。该值存储在A
和内存位置 1 中。不幸的是,内存位置 1 是当前正在执行的命令,因此当需要加密它时,解释器会对查找表进行越界读取(试图在仅涵盖 33 到 126 范围的表中查找对应于 39378 的位置),如果幸运的话,这将产生分段错误。
这种行为在"简单"Malbolge程序中极有可能,因为C和D以相同的值开始并以相同的速率增加。如果您希望旋转指令影响当前正在运行的命令以外的其他内容,则必须以某种方式取消同步它们。最简单的方法通常是j
命令(注意:使用生成的数据指针可能不是特别容易,但至少它可能在代码指针之外的其他位置)。
顺便说一下,实际旋转用户输入比您给出的示例程序花费更多的精力。您必须先将其存储到内存中,并且唯一能够写入依赖于 A 值的内存的操作是p
,这要求有问题的单元格已经具有适当的值(为避免丢失信息,这需要是 29524,您必须通过 Malbolge 的算术生成的值,因为它不能作为原始程序的一部分输入, 即便如此,您最终也会将输入值中的 0 个 trits 与 1 trit 交换)。然后,您必须将数据指针发送回您写入输入的单元格,以便您可以对其运行*
以旋转它。