BF 语言

时隔1年,我胡汉三又回来了😁😁😁😁

OK回归正题,今天在抖音上看到了这个语言,

看了看非常有趣,分享一下,介绍一下BF语言

BF 语言(Brainfuck)是一种极简主义的编程语言,由瑞士计算机科学家马蒂亚斯・兰德尔(Urban Müller)于 1993 年设计。它的设计理念是用最少的指令集实现图灵完备性,因此语法极其简洁,仅有 8 个指令,但编写和理解起来非常困难,主要用于编程挑战、理论研究或娱乐。

BF 语言的核心特点

  1. 极简指令集
    仅包含 8 个单字符指令,所有程序都由这 8 个字符构成:

    • >:指针向右移动(指向内存中下一个单元)
    • <:指针向左移动(指向内存中前一个单元)
    • +:当前内存单元的值加 1
    • -:当前内存单元的值减 1
    • .:输出当前内存单元的值(ASCII 码)
    • ,:从输入读取一个字符,存入当前内存单元
    • [:如果当前内存单元的值为 0,则跳转到对应的]之后
    • ]:如果当前内存单元的值不为 0,则跳转到对应的[之前
  2. 内存模型
    程序运行在一个虚拟的内存带上,由无限个(实际中是有限但足够大)内存单元组成,每个单元初始值为 0,通过一个指针定位当前操作的单元。

  3. 图灵完备性
    尽管指令简单,但 BF 语言可以实现任何可计算的问题(只要内存足够),因此是图灵完备的。

BF 程序示例

1. 输出 "Hello World!"

这是 BF 语言中最经典的程序之一,通过调整内存单元的值来表示字符的 ASCII 码,再用.输出:

brainfuck

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

  • 原理:通过+-调整内存单元的值到目标字符的 ASCII 码(如H的 ASCII 码是 72),再用.输出。
2. 输出单个字符 'A'

A的 ASCII 码是 65,因此可以用 65 个+ followed by .

+++++++++++++++++++++++++++++++++++++++++++++++++.'

BF 语言的应用场景

  • 编程挑战:许多编程竞赛会包含 BF 相关题目,考验逻辑思维和对极简指令的掌握。
  • 理论研究:作为极简编程语言的代表,用于研究图灵机、可计算性等理论。
  • 代码混淆:BF 程序难以阅读,常被用于隐藏代码逻辑(但并非安全手段)。
  • 教育工具:帮助理解程序的本质(内存操作、循环控制等核心概念)。

如何运行 BF 程序

需要使用 BF 编译器,但是网上的现成编译器下载网址404了,所以我用C++编了一个

#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <unordered_map>
using namespace std;

// 使用动态分配的内存,避免栈溢出
vector<unsigned char> tape(30000, 0);  // 传统BF使用30000个单元
unordered_map<int, int> loop_map;      // 存储循环括号的映射关系

// 读取文件内容
string read_file(const string& path) {
    string content;
    ifstream file(path);
    if (file.is_open()) {
        char c;
        while (file.get(c)) {
            content += c;
        }
        file.close();
    }
    return content;
}

// 预处理:建立循环括号的映射
void preprocess_loops(const string& program) {
    stack<int> loop_stack;
    for (int i = 0; i < program.size(); ++i) {
        if (program[i] == '[') {
            loop_stack.push(i);
        } else if (program[i] == ']') {
            if (loop_stack.empty()) {
                cerr << "错误:未匹配的 ']' 在位置 " << i << endl;
                exit(1);
            }
            int start = loop_stack.top();
            loop_stack.pop();
            loop_map[start] = i;
            loop_map[i] = start;
        }
    }
    if (!loop_stack.empty()) {
        cerr << "错误:未匹配的 '[' 在位置 " << loop_stack.top() << endl;
        exit(1);
    }
}

int main(int argc, char* argv[]) {
    string path;
    if (argc > 1) {
        path = argv[1];
    } else {
        cout << "请输入BF程序文件路径:";
        cin >> path;
    }

    string program = read_file(path);
    if (program.empty()) {
        cerr << "错误:无法读取文件或文件为空" << endl;
        return 1;
    }

    preprocess_loops(program);  // 预处理循环结构

    int pointer = 0;            // 数据指针
    int pc = 0;                 // 程序计数器
    int len = program.size();

    while (pc < len) {
        switch (program[pc]) {
            case '>':
                pointer++;
                // 如果超出当前内存大小,动态扩展
                if (pointer >= tape.size()) {
                    tape.push_back(0);
                }
                pc++;
                break;
            
            case '<':
                if (pointer > 0) {
                    pointer--;
                }
                pc++;
                break;
            
            case '+':
                tape[pointer]++;
                pc++;
                break;
            
            case '-':
                tape[pointer]--;
                pc++;
                break;
            
            case '.':
                cout << tape[pointer];
                pc++;
                break;
            
            case ',':
                // 正确处理输入,包括空白字符
                tape[pointer] = cin.get();
                pc++;
                break;
            
            case '[':
                if (tape[pointer] == 0) {
                    // 当前单元为0,跳转到对应的']'
                    pc = loop_map[pc] + 1;
                } else {
                    pc++;
                }
                break;
            
            case ']':
                if (tape[pointer] != 0) {
                    // 当前单元不为0,跳回对应的'['
                    pc = loop_map[pc] + 1;
                } else {
                    pc++;
                }
                break;
            
            default:
                // 忽略所有非BF指令字符(可作为注释)
                pc++;
                break;
        } 
    }

    return 0;
}

OK也是非常通俗易懂,

如何使用

在程序运行时,输入 BF 文件的绝对位置或相对位置就可以了——非常简单吧🙂🙂

并且为了实现让 BF 更加便携,用头文件装了起来

#pragma once
#include <bits/stdc++.h>
using namespace std;

template<const char* Program>
class BF {
private:
    // 使用动态分配的内存,避免栈溢出
    vector<unsigned char> tape;  // 传统BF使用30000个单元
    unordered_map<int, int> loop_map;  // 存储循环括号的映射关系
    int pointer;  // 数据指针
    int pc;       // 程序计数器
    int len;      // 程序长度

    // 预处理:建立循环括号的映射
    void preprocess_loops() {
        stack<int> loop_stack;
        for (int i = 0; i < len; ++i) {
            if (Program[i] == '[') {
                loop_stack.push(i);
            } else if (Program[i] == ']') {
                if (loop_stack.empty()) {
                    cerr << "错误:未匹配的 ']' 在位置 " << i << endl;
                    exit(1);
                }
                int start = loop_stack.top();
                loop_stack.pop();
                loop_map[start] = i;
                loop_map[i] = start;
            }
        }
        if (!loop_stack.empty()) {
            cerr << "错误:未匹配的 '[' 在位置 " << loop_stack.top() << endl;
            exit(1);
        }
    }

public:
    // 构造函数
    BF() : tape(30000, 0), pointer(0), pc(0), len(strlen(Program)) {
        preprocess_loops();  // 预处理循环结构
        run();  // 执行程序
    }

    // 执行BF程序
    void run() {
        while (pc < len) {
            switch (Program[pc]) {
                case '>':
                    pointer++;
                    // 如果超出当前内存大小,动态扩展
                    if (pointer >= tape.size()) {
                        for (int i = 0; i < 3; ++i) {
                            tape.push_back(0);
                        }
                    }
                    pc++;
                    break;
                
                case '<':
                    if (pointer > 0) {
                        pointer--;
                    }
                    pc++;
                    break;
                
                case '+':
                    tape[pointer]++;
                    pc++;
                    break;
                
                case '-':
                    tape[pointer]--;
                    pc++;
                    break;
                
                case '.':
                    cout << tape[pointer];
                    pc++;
                    break;
                
                case ',':
                    // 正确处理输入,包括空白字符
                    tape[pointer] = cin.get();
                    pc++;
                    break; 
                case '[':
                    if (tape[pointer] == 0) {
                        // 当前单元为0,跳转到对应的']'
                        pc = loop_map[pc] + 1;
                    } else {
                        pc++;
                    }
                    break;
                
                case ']':
                    if (tape[pointer] != 0) {
                        // 当前单元不为0,跳回对应的'['
                        pc = loop_map[pc] + 1;
                    } else {
                        pc++;
                    }
                    break;
                
                default:
                    // 忽略所有非BF指令字符(可作为注释)
                    pc++;
                    break;
            } 
        }
    }
};

注意:我这里使用了

#include<bits/stdc++.h>
//万能头
using namespace std;

如果想再无万能头的环境下(visual studio)运行可以将万能头改为

#include <iostream>   // 用于输入输出操作(cin, cout, cerr)
#include <vector>     // 用于动态内存带(tape)的实现
#include <unordered_map>  // 用于存储循环括号的映射关系
#include <stack>      // 用于预处理阶段的括号匹配
#include <cstring>    // 用于strlen函数计算程序长度

OK以上就是全部内容,记得点赞😭😭😭😭😭😭😭😭😭😭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值