1. 基础
要理解 Brainfuck,您必须想象由0
每个单元初始化的无限数组。
...[0][0][0][0][0]...
当brainfuck 程序启动时,它指向任何单元格。
...[0][0][*0*][0][0]...
如果将指针向右>
移动,则将指针从单元格 X 移动到单元格 X+1
...[0][0][0][*0*][0]...
如果你增加单元格值+
,你会得到:
...[0][0][0][*1*][0]...
如果再次增加单元格值,+
您将获得:
...[0][0][0][*2*][0]...
如果你减少单元格值-
,你会得到:
...[0][0][0][*1*][0]...
如果将指针向左<
移动,则将指针从单元格 X 移动到单元格 X-1
...[0][0][*0*][1][0]...
2.输入
要读取字符,请使用逗号,
。它的作用是:从标准输入读取字符并将其十进制 ASCII 代码写入实际单元格。
看看ASCII 表。例如,十进制代码!
是33
,a
而是97
。
好吧,让我们想象一下您的 BF 程序内存如下所示:
...[0][0][*0*][0][0]...
假设标准输入代表a
,如果你使用逗号,
操作符,BF 所做的就是将a
十进制 ASCII 码读97
入内存:
...[0][0][*97*][0][0]...
你通常想这样想,但事实要复杂一些。事实是 BF 读取的不是字符而是字节(无论那个字节是什么)。让我给你看例子:
在linux中
$ printf ł
印刷:
ł
这是特定的波兰字符。此字符未通过 ASCII 编码进行编码。在这种情况下,它是 UTF-8 编码,因此它曾经在计算机内存中占用超过一个字节。我们可以通过制作一个十六进制转储来证明这一点:
$ printf ł | hd
这表明:
00000000 c5 82 |..|
零点偏移。82
是第一个和c5
第二个字节表示ł
(为了我们将阅读它们)。|..|
是在这种情况下不可能的图形表示。
好吧,如果您将ł
作为输入传递给读取单字节的 BF 程序,程序存储器将如下所示:
...[0][0][*197*][0][0]...
为什么197
?那么197
十进制是c5
十六进制。似曾相识?当然。ł
这是!的第一个字节
3. 输出
要打印字符,请使用 dot.
它的作用是:假设我们将实际单元格值视为十进制 ASCII 码,将相应的字符打印到标准输出。
好吧,让我们想象一下您的 BF 程序内存如下所示:
...[0][0][*97*][0][0]...
如果您现在使用点 (.) 运算符,BF 所做的是打印:
一个
因为a
ASCII 中的十进制代码是97
.
所以例如像这样的BF程序(97加2点):
++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++..
将其指向的单元格的值增加到 97 并打印 2 次。
啊
4.循环
在 BF 循环中包括循环开始[
和循环结束]
。您可以认为这就像在 C/C++ 中,条件是实际单元格值。
看看下面的BF程序:
++[]
++
将实际单元格值增加两次:
...[0][0][*2*][0][0]...
[]
就像,所以while(2) {}
它是无限循环。
假设我们不希望这个循环是无限的。例如,我们可以这样做:
++[-]
因此,每次循环循环时,它都会减少实际的单元格值。一旦实际单元格值0
循环结束:
...[0][0][*2*][0][0]... loop starts
...[0][0][*1*][0][0]... after first iteration
...[0][0][*0*][0][0]... after second iteration (loop ends)
让我们考虑另一个有限循环的例子:
++[>]
此示例显示,我们尚未在循环开始的单元格处完成循环:
...[0][0][*2*][0][0]... loop starts
...[0][0][2][*0*][0]... after first iteration (loop ends)
然而,在我们开始的地方结束是一种很好的做法。为什么 ?因为如果循环结束它开始的另一个单元格,我们不能假设单元格指针将在哪里。老实说,这种做法让brainfuck 少了brainfuck。