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

OK回归正题,今天在抖音上看到了这个语言,
看了看非常有趣,分享一下,介绍一下BF语言
BF 语言(Brainfuck)是一种极简主义的编程语言,由瑞士计算机科学家马蒂亚斯・兰德尔(Urban Müller)于 1993 年设计。它的设计理念是用最少的指令集实现图灵完备性,因此语法极其简洁,仅有 8 个指令,但编写和理解起来非常困难,主要用于编程挑战、理论研究或娱乐。
BF 语言的核心特点
-
极简指令集
仅包含 8 个单字符指令,所有程序都由这 8 个字符构成:>:指针向右移动(指向内存中下一个单元)<:指针向左移动(指向内存中前一个单元)+:当前内存单元的值加 1-:当前内存单元的值减 1.:输出当前内存单元的值(ASCII 码),:从输入读取一个字符,存入当前内存单元[:如果当前内存单元的值为 0,则跳转到对应的]之后]:如果当前内存单元的值不为 0,则跳转到对应的[之前
-
内存模型
程序运行在一个虚拟的内存带上,由无限个(实际中是有限但足够大)内存单元组成,每个单元初始值为 0,通过一个指针定位当前操作的单元。 -
图灵完备性
尽管指令简单,但 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以上就是全部内容,记得点赞😭😭😭😭😭😭😭😭😭😭
23万+

被折叠的 条评论
为什么被折叠?



