Brainfuck Hello World是如何工作的?
有人把这个发给我,并声称这是一个在Brainfuck你好的世界(我希望如此…)
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. 我知道它通过移动一个指针和增量和减量的东西,它的基础知识…
但是我仍然想知道,它是如何工作的? 如何在屏幕上打印任何东西? 它如何编码文本? 我根本不懂…
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.input
 阅读字符,你使用逗号, 。 它所做的是: 从标准input读取字符,并将其十进制ASCII码写入实际单元。 
 看看ASCII表 。 例如,十进制代码! 是33 ,而a是97 。 
那么,让我们想象你的BF程序内存如下所示:
 ...[0][0][*0*][0][0]... 
 假设标准input代表a ,如果使用逗号,运算符,BF所做的是读取十进制ASCII码97到内存: 
 ...[0][0][*97*][0][0]... 
你一般都想这样想,然而事实有点复杂。 事实是,BF不读取一个字符,而是一个字节(不pipe那个字节是什么)。 让我给你看看例子:
在Linux中
 $ printf ł 
打印:
 ł 
这是特定的波兰字符。 这个字符不是用ASCII编码编码的。 在这种情况下,它是UTF-8编码,因此在计算机内存中使用多于一个字节。 我们可以通过hex转储来certificate它:
 $ printf ł | hd 
这表明:
 00000000 c5 82 |..| 
 零点被抵消。  82是第一个, c5是代表ł第二个字节(我们将读取它们)。 |..| 是在这种情况下不可能的graphics表示。 
那么,如果你将BF作为input传递给读取单字节的BF程序,则程序存储器将如下所示:
 ...[0][0][*197*][0][0]... 
 为什么197 ? 那么197十进制是c5hex。 似乎很熟悉? 当然。 这是ł的第一个字节! 
3.输出
 要打印你使用点的字符. 它所做的是: 假设我们将实际的单元格值视为十进制ASCII码,则将相应的字符打印到标准输出。 
那么,让我们想象你的BF程序内存如下所示:
 ...[0][0][*97*][0][0]... 
如果您现在使用点(。)运算符,则BF所做的是打印:
一个
 因为ASCII中a十进制代码是97 。 
所以比如BF程序就是这样的(97加2个点):
++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++ ..
将增加的单元格的值指向97,并打印出来2次。
AA
4.循环
  BF循环由循环开始[和循环结束] 。 你可以认为它就像在C / C ++中那样,条件是实际的单元格值。 
看看下面的BF程序:
 ++[] 
  ++实际单元格值增加两次: 
 ...[0][0][*2*][0][0]... 
 而[]就像while(2) {} ,所以是无限循环。 
假设我们不希望这个循环是无限的。 我们可以做例如:
 ++[-] 
 所以每次循环循环都会减less实际的单元格值。 一旦实际的单元格值为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减lessbrainfuck。
维基百科有一个注释版本的代码。
 +++++ +++++ initialize counter (cell #0) to 10 [ use loop to set the next four cells to 70/100/30/10 > +++++ ++ add 7 to cell #1 > +++++ +++++ add 10 to cell #2 > +++ add 3 to cell #3 > + add 1 to cell #4 <<<< - decrement counter (cell #0) ] > ++ . print 'H' > + . print 'e' +++++ ++ . print 'l' . print 'l' +++ . print 'o' > ++ . print ' ' << +++++ +++++ +++++ . print 'W' > . print 'o' +++ . print 'r' ----- - . print 'l' ----- --- . print 'd' > + . print '!' > . print '\n' 
 回答你的问题,和. 字符用于I / O。 文本是ASCII。 
维基百科的文章也进一步深入。
第一行通过简单地从0递增10次来初始化
a[0] = 10来自第2行的循环有效地设置数组的初始值:a[1] = 70(接近72,字符' H'),a[2] = 100(接近101或'e'),a[3] = 30(接近32,空格代码)和a[4] = 10(换行符)。 循环的工作原理是,每次通过每个单元的循环增加10次,分别向单元a[1],a[2],a[3]和a[4]7,10,3和a[4]给出a[1]=70等)。 循环结束后,a[0]为零。>++.然后将指针移到a[1]保存有70a[1],将它加2(产生72,这是大写字母H的ASCII字符代码),并输出它。下一行将数组指针移动到
a[2]并将其加1,产生101,然后输出小写的'e'。由于'l'恰好是'e'之后的第七个字母,为了输出'll',另外七个被添加(
+++++++)到a[2]并且结果被输出两次。'o'是'l'之后的第三个字母,所以
a[2]再增加三次并输出结果。程序的其余部分以相同的方式继续。 对于空格和大写字母,根据需要select不同的数组单元格并递增或递减。
为了回答它如何知道打印内容的问题,我已经将ASCII值的计算添加到打印发生的代码的右侧:
 > just means move to the next cell < just means move to the previous cell + and - are used for increment and decrement respectively. The value of the cell is updated when the increment/decrement happens +++++ +++++ initialize counter (cell #0) to 10 [ use loop to set the next four cells to 70/100/30/10 > +++++ ++ add 7 to cell #1 > +++++ +++++ add 10 to cell #2 > +++ add 3 to cell #3 > + add 1 to cell #4 <<<< - decrement counter (cell #0) ] > ++ . print 'H' (ascii: 70+2 = 72) //70 is value in current cell. The two +s increment the value of the current cell by 2 > + . print 'e' (ascii: 100+1 = 101) +++++ ++ . print 'l' (ascii: 101+7 = 108) . print 'l' dot prints same thing again +++ . print 'o' (ascii: 108+3 = 111) > ++ . print ' ' (ascii: 30+2 = 32) << +++++ +++++ +++++ . print 'W' (ascii: 72+15 = 87) > . print 'o' (ascii: 111) +++ . print 'r' (ascii: 111+3 = 114) ----- - . print 'l' (ascii: 114-6 = 108) ----- --- . print 'd' (ascii: 108-8 = 100) > + . print '!' (ascii: 32+1 = 33) > . print '\n'(ascii: 10) 
 所有的答案是彻底的,但他们缺乏一个小细节:印刷。 在build立你的brainfuck翻译,你也考虑到字符.  ,这实际上是一个打印语句在brainfuck中看起来像什么。 那么你的脑子翻译应该做什么,每当遇到一个. 字符它打印当前指向的字节。 
例:
 假设你有 – > char *ptr = [0] [0] [0] [97] [0] …如果这是一个brainfuck声明: >>>. 你的指针应该被移动3个空格到右侧: [97] ,所以现在*ptr = 97 ,在这之后你的翻译遇到了一个.  ,那就应该打电话 
 write(1, ptr, 1) 
 或任何等价的打印语句来打印当前指向的字节,其值为97 ,字母a将打印在std_output 。