BrainFuck入门

在现代编程语言日益复杂的今天,有一种语言以其极端的简洁和极高的晦涩程度脱颖而出,它就是——Brainfuck。这门语言看似是个玩笑,却是一门**图灵完备(Turing-complete)**的编程语言,意味着它可以完成任何计算任务(只要你愿意花足够的时间和精力)。

What the Brainfuck?

Brainfuck由Urban Müller于1993年设计,仅包含8个字符

> < + - . , [ ]

所有其他字符都会被忽略(这意味着你可以自由地添加注释,只要避开这8个字符)。

Brainfuck操作的是一个字节数组(通常为30,000字节,初始全为0)和一个指针(称为数据指针),通过这8个字符的组合完成读写、逻辑、循环等操作。

Brainfuck的工作原理(类图灵机)

Brainfuck的运行模型可类比为一种非常简化的图灵机

  • 一个无限长(在实际中为固定长度,如30,000字节)的内存带,每个单元是一个字节(0-255)。

  • 一个数据指针,默认指向内存的第一个字节。

  • 程序由8条基本指令组成,每条指令对应一个操作。

  • 控制结构通过[]实现循环,类似汇编语言中的条件跳转。

Brainfuck的8个指令:

指令 作用
> 数据指针右移一格
< 数据指针左移一格
+ 当前单元值加1(255后回到0)
- 当前单元值减1(0后回到255)
. 输出当前单元的ASCII字符
, 读取一个字符存入当前单元
[ 如果当前单元值为0,跳转到对应的]
] 如果当前单元值不为0,跳转回对应的[

示例程序

1. Hello World!

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.

2. 简单加法(输入两个字符,输出它们的和)

,>,<[->+<]>.

 

 

  • ,>:读取两个字符到相邻内存单元

  • <[->+<]>:将第一个字符的值加到第二个中

  • .:输出第二个单元(现在是两者之和)

3. 乘法(3×2)

+++      // 3
> ++     // 2
<[->+>+<<]>>[-<<+>>]<
.

这个程序较为复杂,利用一个加法循环模拟乘法。

用C语言编写的Brainfuck解释器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define TAPE_SIZE 30000
#define MAX_CODE_SIZE (1024 * 1024) /* 1MB 限制 */

/* 检查 Brainfuck 源码括号是否匹配 */
static int check_brackets(const char *code) {
    int balance = 0;
    const char *p;
    for (p = code; *p; ++p) {
        if (*p == '[') balance++;
        else if (*p == ']') {
            balance--;
            if (balance < 0) return 0;
        }
    }
    return balance == 0;
}

/* Brainfuck 解释器主循环 */
void run_bf(const char *code) {
    unsigned char tape[TAPE_SIZE] = {0};
    unsigned char *ptr = tape;
    const char *pc = code;
    while (*pc) {
        switch (*pc) {
            case '>':
                if (ptr - tape >= TAPE_SIZE - 1) {
                    fprintf(stderr, "Error: Tape pointer overflow.\n");
                    exit(EXIT_FAILURE);
                }
                ptr++;
                break;
            case '<':
                if (ptr == tape) {
                    fprintf(stderr, "Error: Tape pointer underflow.\n");
                    exit(EXIT_FAILURE);
                }
                ptr--;
                break;
            case '+': (*ptr)++; break;
            case '-': (*ptr)--; break;
            case '.': putchar(*ptr); break;
            case ',': {
                int c = getchar();
                *ptr = (c == EOF) ? 0 : (unsigned char)c;
                break;
            }
            case '[':
                if (*ptr == 0) {
                    int loop = 1;
                    while (loop > 0) {
                        pc++;
                        if (*pc == '\0') {
                            fprintf(stderr, "Error: Unmatched '['.\n");
                            exit(EXIT_FAILURE);
                        }
                        if (*pc == '[') loop++;
                        else if (*pc == ']') loop--;
                    }
                }
                break;
            case ']':
                if (*ptr != 0) {
                    int loop = 1;
                    while (loop > 0) {
                        pc--;
                        if (pc < code) {
                            fprintf(stderr, "Error: Unmatched ']'.\n");
                            exit(EXIT_FAILURE);
                        }
                        if (*pc == ']') loop++;
                        else if (*pc == '[') loop--;
                    }
                }
                break;
            default:
                /* 忽略非 Brainfuck 指令字符 */
                break;
        }
        pc++;
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <brainfuck source file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    FILE *f = fopen(argv[1], "rb");
    if (!f) {
        fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }

    if (fseek(f, 0, SEEK_END) != 0) {
        fprintf(stderr, "Error seeking file.\n");
        fclose(f);
        return EXIT_FAILURE;
    }
    long size = ftell(f);
    if (size < 0 || size > MAX_CODE_SIZE) {
        fprintf(stderr, "Error: File size invalid or too large.\n");
        fclose(f);
        return EXIT_FAILURE;
    }
    rewind(f);

    char *code = (char *)malloc((size_t)size + 1);
    if (!code) {
        fprintf(stderr, "Memory allocation failed.\n");
        fclose(f);
        return EXIT_FAILURE;
    }
    if (fread(code, 1, (size_t)size, f) != (size_t)size) {
        fprintf(stderr, "Error reading file.\n");
        free(code);
        fclose(f);
        return EXIT_FAILURE;
    }
    code[size] = '\0';
    fclose(f);

    if (!check_brackets(code)) {
        fprintf(stderr, "Error: Brackets do not match in source code.\n");
        free(code);
        return EXIT_FAILURE;
    }

    run_bf(code);
    free(code);
    return EXIT_SUCCESS;
}

Brainfuck虽然极其简陋,但正因如此,它是学习语言设计、图灵机、解释器构建的绝佳练习场。它也提醒我们,编程语言并不一定需要庞大的库或语法糖才具备强大功能。哪怕只有8个字符,也足以构建一个通用计算系统。

阅读剩余
THE END