小型C编译器源码解析:从词法分析到代码生成的实现指南

简介:这份资源是一套小型C编译器的完整源代码,面向希望深入理解编译原理、动手实践编译流程的开发者与计算机专业学生。它把词法分析、语法分析、语义分析、优化与代码生成等阶段落实为可读可改的代码,适合作为学习编译器内部运作的入门项目。压缩包共86个文件,约210KB,以50个c源文件和12个h头文件为主体,另有8个p、5个txt说明、3个bat批处理、2个cfg配置及makefile等构建文件,覆盖词法、语法、符号表、表达式、代码生成与优化等模块,并针对386与68系列提供不同后端实现。目前已有479人学习下载。通过阅读和修改这些源码,读者可以掌握标记识别、抽象语法树构建、符号表管理、类型检查与目标代码生成的具体做法,理解指针、结构体、函数调用等C语言特性的处理方式,并积累调试编译错误、优化代码性能的实践经验,为自行开发编译器或深入研究底层机制打下基础。

1. 从一份能跑通的小型C编译器源码说起

如果你写过 C 语言,大概率好奇过一件事:我写的int main() { return 0; }到底是怎么变成机器能跑的程序的?大多数人止步于“编译器是个黑匣子”。但手头这份小型 C 编译器源码,恰好把黑匣子拆开了——它用几千行代码实现了词法分析、语法分析、语义检查、中间代码生成和目标代码输出,能编译一个 C 语言子集,生成可执行的汇编或目标文件。适合谁?适合想搞懂编译原理但被龙书劝退的人,适合想给语言加语法特性却不知道从哪下手的开发者,也适合拿它当编译原理课程设计参考的学生。它不追求完整支持 ISO C,而是把编译流程的每个阶段都摊开给你看,改起来心里有底。

2. 编译器骨架拆解:从源码目录到四阶段流水线

2.1 目录结构与模块职责

拿到源码先别急着编译,花五分钟把目录扫一遍,后面改代码能省一半时间。这类小型编译器的目录通常长这样:

src/ lexer.c # 词法分析:字符流 → token 流 parser.c # 语法分析:token 流 → AST ast.c / ast.h # 抽象语法树节点定义与操作 sema.c # 语义分析:类型检查、符号表 codegen.c # 代码生成:AST → 汇编/字节码 symtab.c # 符号表:作用域与变量管理 main.c # 驱动:串联各阶段 include/ token.h # token 类型枚举 ast.h # AST 节点结构 tests/ *.c # 测试用例 Makefile

这个划分对应编译器的经典四阶段:词法分析 → 语法分析 → 语义分析 → 代码生成。每个阶段之间用明确的数据结构衔接——token 流、AST、带类型标注的 AST、目标代码。好处是你可以单独替换某一阶段,比如把代码生成从 x86 汇编换成自定义字节码,其他部分不用动。

常见做法是每个阶段暴露一个入口函数,比如lex_all()、parse()、sema_check()、codegen(),main.c里按顺序调用。这种设计对二次开发非常友好,你想加个新语法,改 parser 和 codegen 就行,lexer 基本不动。

2.2 词法分析:手写扫描器怎么处理 token

词法分析器负责把源代码字符串切成一个个 token。这份源码用的是手写扫描器,不是 lex/flex 生成的。手写的好处是逻辑透明,加关键字、改运算符优先级都直接改代码。

核心逻辑是一个循环:跳过空白和注释,根据当前字符判断 token 类型,然后消费字符直到 token 结束。

// lexer.c 核心扫描循环(简化示意) Token next_token(Lexer *lex) { skip_whitespace_and_comments(lex); // 跳过空格、换行、// 和 /* */ char c = peek(lex); if (isalpha(c) || c == '_') { return scan_identifier(lex); // 标识符或关键字 } if (isdigit(c)) { return scan_number(lex); // 整数/浮点字面量 } return scan_operator(lex); // 运算符和分隔符 }

scan_identifier会读取连续字母数字下划线,然后查关键字表——如果是int、if、while就返回对应关键字 token,否则返回标识符 token。scan_number处理十进制和十六进制,注意溢出检查。scan_operator要处理多字符运算符,比如==、!=、<=、&&,不能只读一个字符就返回。

参数说明:Lexer结构体里通常维护source(源码字符串)、pos(当前读取位置)、line(行号,报错用)。peek()看当前字符但不前进,advance()前进一格。报错时用line定位,输出类似error at line 12: unexpected character '@'。

这里有个容易翻车的地方:注释处理。//单行注释要读到行尾,/* */块注释要处理跨行和未闭合的情况。如果块注释没闭合,扫描器应该报错而不是死循环。

2.3 语法分析:递归下降构建 AST

语法分析器把 token 流变成抽象语法树。这份源码用的是递归下降分析法,每个语法规则对应一个函数。比如parse_expression()处理表达式,parse_statement()处理语句,parse_declaration()处理变量声明。

递归下降的核心是优先级分层。表达式通常分成好几层:赋值 → 逻辑或 → 逻辑与 → 相等比较 → 关系比较 → 加减 → 乘除 → 一元 → 后缀 → 基本表达式。每层函数调用下一层,这样自然处理了运算符优先级。

// parser.c 表达式解析的优先级分层(简化示意) ASTNode *parse_expression(Parser *p) { return parse_assignment(p); // 最低优先级:赋值 } ASTNode *parse_assignment(Parser *p) { ASTNode *left = parse_logical_or(p); if (match(p, TOKEN_ASSIGN)) { ASTNode *right = parse_assignment(p); // 右结合 return make_assign_node(left, right); } return left; } ASTNode *parse_logical_or(Parser *p) { ASTNode *left = parse_logical_and(p); while (match(p, TOKEN_OR)) { // || 左结合 ASTNode *right = parse_logical_and(p); left = make_binary_node(OP_LOGICAL_OR, left, right); } return left; } // ... 依次往下:parse_logical_and → parse_equality → parse_relational // → parse_additive → parse_multiplicative → parse_unary → parse_primary

match(p, type)检查当前 token 是否匹配,匹配则消费并返回真。make_binary_node创建二叉树节点。注意赋值是右结合的,所以parse_assignment递归调用自己;而加减乘除是左结合的,用while循环。

AST 节点通常用 tagged union 表示:

// ast.h 节点定义(简化示意) typedef enum { AST_NUM, AST_VAR, AST_BINARY, AST_ASSIGN, AST_IF, AST_WHILE, AST_RETURN, AST_BLOCK } ASTKind; struct ASTNode { ASTKind kind; union { int num; // AST_NUM char *var_name; // AST_VAR struct { int op; struct ASTNode *left, *right; } binary; struct { struct ASTNode *target, *value; } assign; struct { struct ASTNode *cond, *then_body, *else_body; } if_stmt; struct { struct ASTNode *cond, *body; } while_stmt; struct { struct ASTNode *expr; } return_stmt; struct { struct ASTNode **stmts; int count; } block; } u; };

这种设计内存紧凑,但访问时要先看kind再取对应字段。改的时候注意别取错 union 成员,否则就是玄学 bug。

2.4 语义分析与符号表:类型检查和作用域管理

语法分析只保证结构正确,语义分析才检查“意思对不对”。比如变量是否声明、类型是否匹配、函数调用参数个数对不对。

符号表是语义分析的核心数据结构。常见做法是用哈希表或链表栈,支持作用域嵌套。进入一个块就压入新作用域,离开就弹出。

// symtab.c 符号表接口(简化示意) typedef struct Symbol { char *name; Type *type; int is_global; struct Symbol *next; // 链表解决哈希冲突 } Symbol; typedef struct Scope { Symbol *buckets[SYMTAB_SIZE]; struct Scope *parent; // 指向外层作用域 } Scope; Symbol *symtab_lookup(Scope *scope, const char *name) { for (Scope *s = scope; s != NULL; s = s->parent) { Symbol *sym = hash_find(s->buckets, name); if (sym) return sym; // 找到最近声明的 } return NULL; // 未声明 } void symtab_insert(Scope *scope, const char *name, Type *type) { if (hash_find(scope->buckets, name)) { error("redeclaration of '%s'", name); // 同一作用域重复声明 } hash_insert(scope->buckets, name, type); }

symtab_lookup从当前作用域往外层找,实现变量遮蔽(shadowing)。symtab_insert只检查当前作用域是否重复,允许内层遮蔽外层同名变量。

语义分析遍历 AST,对每个节点做检查:变量引用查符号表,二元运算检查两边类型是否兼容,函数调用检查参数个数和类型。发现错误就报错并记录,通常不立即停止,而是收集多个错误一次性输出,方便修。

2.5 代码生成:从 AST 到汇编的映射

代码生成阶段把带类型标注的 AST 翻译成目标代码。这份源码可能生成 x86 汇编或自定义字节码。以 x86 为例,核心思路是后序遍历 AST,用栈式求值。

// codegen.c 表达式代码生成(简化示意) void gen_expr(ASTNode *node) { switch (node->kind) { case AST_NUM: emit(" movl $%d, %%eax\n", node->u.num); // 立即数加载 break; case AST_VAR: emit(" movl %s(%%rip), %%eax\n", node->u.var_name); // 变量加载 break; case AST_BINARY: gen_expr(node->u.binary.left); emit(" pushq %%rax\n"); // 左操作数入栈 gen_expr(node->u.binary.right); emit(" popq %%rcx\n"); // 右操作数出栈到 rcx switch (node->u.binary.op) { case OP_ADD: emit(" addl %%ecx, %%eax\n"); break; case OP_SUB: emit(" subl %%ecx, %%eax\n"); break; case OP_MUL: emit(" imull %%ecx, %%eax\n"); break; case OP_DIV: emit(" cltd\n idivl %%ecx\n"); break; } break; } }

逻辑说明:先算左子树,结果在%eax,压栈保存;再算右子树,结果在%eax;弹出左操作数到%ecx;根据运算符做运算,结果留在%eax。这就是经典的栈式代码生成。

参数说明:emit()是输出格式化字符串到汇编文件的辅助函数。%rip相对寻址用于全局变量。局部变量通常用%rbp偏移寻址,需要在函数序言里分配栈帧。

语句的代码生成:if生成条件跳转和标签,while生成循环标签和回跳,return把值放到%eax然后跳转到函数尾声。标签用全局计数器生成唯一名字,比如.L1、.L2。

注意:代码生成最容易翻车的地方是寄存器分配和栈对齐。x86-64 调用函数时要求 16 字节栈对齐,如果生成的代码在call前栈没对齐,程序可能在某些 libc 函数里崩溃。调试时先用简单表达式测试,再逐步加复杂语句。

3. 把源码跑起来:构建、测试与调试环境

3.1 构建系统与依赖

这类小型编译器通常只依赖标准 C 库和系统汇编器/链接器。构建方式一般是 Makefile:

# 常见构建命令 make # 编译生成编译器可执行文件 make test # 跑测试用例 make clean # 清理中间文件

如果源码里没有 Makefile,手动编译也不复杂:

gcc -O2 -Wall -Wextra -o mycc src/*.c

-O2开优化,-Wall -Wextra开警告,编译器的代码质量要求高,警告最好全清。依赖方面,只需要gcc或clang,以及as(汇编器)和ld(链接器),Linux 和 macOS 自带。

Windows 上建议用 WSL 或 MSYS2,因为生成的汇编是 AT&T 语法,直接调 GNU as 最省事。

3.2 编译一个测试程序

编译器构建好后,用它编译一个简单 C 文件:

# 假设编译器叫 mycc echo 'int main() { return 42; }' > test.c ./mycc test.c -o test.s # 生成汇编 gcc test.s -o test # 汇编链接 ./test; echo $? # 输出 42

如果编译器直接生成可执行文件,那./mycc test.c -o test一步到位。但多数教学编译器只生成汇编,链接交给系统 gcc,这样实现简单,也方便调试。

测试用例建议从简到繁:先测常量返回,再测算术运算,然后变量声明和赋值,接着if/while,最后函数调用。每通过一类再加下一类,出问题容易定位。

3.3 调试编译器本身的方法

调试编译器比调试普通程序麻烦,因为 bug 可能在任何阶段。常用手段:

打印中间表示。在词法分析后打印 token 流,语法分析后打印 AST,语义分析后打印带类型的 AST。很多编译器源码里已经有--dump-tokens、--dump-ast之类的选项,没有就自己加。

// 在 main.c 里加调试输出 if (debug_tokens) dump_tokens(token_list); if (debug_ast) dump_ast(ast_root);

用 gdb 断点。在parse_expression或gen_expr下断点,看 AST 结构对不对。编译器的递归调用栈比较深,gdb 的bt命令能看清调用链。

对比法。同一个 C 程序,用 gcc 编译跑一遍,用自己的编译器编译跑一遍,输出不一致就缩小范围。先对比汇编代码,再对比 AST。

最小复现。遇到 bug 先把测试用例删到最小,比如从 50 行删到 5 行还能复现,问题范围就小很多。

4. 避坑与常见问题排查

4.1 词法分析把关键字当标识符

现象:int main里的int被当成变量名,语法分析报“expected identifier”。

原因:scan_identifier读完字符串后没查关键字表,或者关键字表里漏了某个关键字。

解决:检查scan_identifier末尾是否调用lookup_keyword(),关键字表是否包含所有保留字。加个单元测试,对每个关键字断言返回正确 token 类型。

4.2 表达式优先级错误导致计算结果不对

现象:1 + 2 * 3算出来是 9 而不是 7。

原因:递归下降的优先级分层写错了,比如parse_additive直接调用了parse_primary,跳过了parse_multiplicative。

解决:画一张优先级表,从低到高排列,确保每层函数只调用下一层。用1 + 2 * 3、(1 + 2) * 3、1 < 2 == 1这类用例验证。

4.3 符号表作用域没弹出导致变量泄漏

现象:内层块声明的变量在外层也能访问,或者同名变量报重复声明。

原因:进入块时压入新作用域,但离开块时忘了弹出;或者symtab_insert检查了所有作用域而不是只检查当前。

解决:在parse_block或语义分析的块处理里,确保scope_enter()和scope_exit()成对出现。用{ int x; } { int x; }测试,两个x应该互不干扰。

4.4 代码生成栈不平衡导致段错误

现象:编译出的程序运行时 segfault,或者返回值莫名其妙。

原因:gen_expr里push和pop数量不匹配,或者函数调用前后栈没恢复。

解决:在gen_expr入口和出口记录栈深度,断言进出时深度一致。函数调用要按 ABI 处理参数寄存器(%rdi、%rsi等)和栈对齐。

4.5 汇编语法错误但编译器不报错

现象:gcc test.s报汇编错误,但./mycc没报任何错。

原因:编译器只负责生成汇编文本,不检查语法。生成的汇编有错,要到汇编阶段才暴露。

解决:在编译器里加一个可选的“汇编验证”步骤,生成后调as试汇编,失败就报错并输出出错行。或者养成习惯,每次生成汇编后手动gcc -c检查。

5. 二次开发:加一个for循环和短路求值

拿到这份源码,最想干的事大概是加语法特性。我一般从两个方向入手:加一个for循环,或者把&&/||改成短路求值。这两个改动覆盖了编译器前端到后端的主要环节,做完对整体架构就熟了。

5.1 加for循环:从 token 到代码生成

for循环的语法是for (init; cond; step) body。改动分四步:

第一步,加关键字 token。在token.h的枚举里加TOKEN_FOR,在关键字表里注册"for"。

第二步,加 AST 节点。在ast.h里加:

struct { ASTNode *init, *cond, *step, *body; } for_stmt;

第三步,加解析函数。在parser.c里:

ASTNode *parse_for(Parser *p) { expect(p, TOKEN_FOR); expect(p, TOKEN_LPAREN); ASTNode *init = parse_expression(p); // 初始化 expect(p, TOKEN_SEMICOLON); ASTNode *cond = parse_expression(p); // 条件 expect(p, TOKEN_SEMICOLON); ASTNode *step = parse_expression(p); // 步进 expect(p, TOKEN_RPAREN); ASTNode *body = parse_statement(p); // 循环体 return make_for_node(init, cond, step, body); }

然后在parse_statement里遇到TOKEN_FOR时调用parse_for。

第四步,加代码生成。for可以脱糖成while:

// codegen.c 里把 for 当 while 处理 case AST_FOR: gen_expr(node->u.for_stmt.init); emit(".L_cond_%d:\n", label_id); gen_expr(node->u.for_stmt.cond); emit(" cmpl $0, %%eax\n"); emit(" je .L_end_%d\n", label_id); gen_stmt(node->u.for_stmt.body); gen_expr(node->u.for_stmt.step); emit(" jmp .L_cond_%d\n", label_id); emit(".L_end_%d:\n", label_id); label_id++; break;

改完用for (int i = 0; i < 10; i = i + 1) sum = sum + i;测试,结果应该是 45。

5.2 短路求值:让&&和||符合 C 语义

默认的二元运算代码生成会把&&两边都算一遍,但 C 语言要求短路:a && b如果a为假,b不执行。改法是在代码生成阶段特殊处理:

case OP_LOGICAL_AND: gen_expr(node->u.binary.left); emit(" cmpl $0, %%eax\n"); emit(" je .L_false_%d\n", label_id); // 左为假,直接跳假 gen_expr(node->u.binary.right); emit(" cmpl $0, %%eax\n"); emit(" je .L_false_%d\n", label_id); emit(" movl $1, %%eax\n"); // 两边都真,结果 1 emit(" jmp .L_end_%d\n", label_id); emit(".L_false_%d:\n", label_id); emit(" movl $0, %%eax\n"); // 结果 0 emit(".L_end_%d:\n", label_id); label_id++; break;

||类似,只是左为真时直接跳真。改完用int x = 0; if (x != 0 && 10 / x > 1) ...测试,不应该除零崩溃。

5.3 验证方法与回归测试

每加一个特性,跑三类测试:新特性专项测试(for循环的各种边界,短路求值的副作用验证)、原有测试回归(确保没破坏已有功能)、对比测试(同一程序用 gcc 和自研编译器编译,输出一致)。

我习惯在tests/下按特性建目录,每个用例一个.c文件加一个.expected文件存期望输出,写个脚本自动跑。这样改代码后一条命令就知道有没有翻车。

从那以后我每次动编译器前端,都强制走一遍“token dump → AST dump → 汇编对比”三步,宁可多花十分钟,也不愿在几百行汇编里找后悔药。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

MAS 激活脚本完整指南:4 种方法免费激活 Windows 与 Office

MAS 激活脚本完整指南:4 种方法免费激活 Windows 与 Office

MAS 激活脚本完整指南&#xff1a;4 种方法免费激活 Windows 与 Office 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting.…

2026/10/20 2:22:27 阅读更多 →
Java AI Agent生态全景 —— Spring AI vs LangGraph4j vs LangChain4j

Java AI Agent生态全景 —— Spring AI vs LangGraph4j vs LangChain4j

引子&#xff1a;一个后端团队的框架选型困局 你所在的Java后端团队接到一个需求&#xff1a;给现有的Spring Boot微服务集群加一个“智能运维助手”——它能查询服务健康状态、分析异常日志、执行简单的运维操作&#xff08;重启服务、扩容、回滚部署&#xff09;&#xff0c…

2026/10/20 2:21:27 阅读更多 →
DevEco Studio Debug调试(一)

DevEco Studio Debug调试(一)

一、设置断点调试过程中可以添加断点&#xff1a;断点设置很简单&#xff1a;在代码行号左侧点击即可添加断点设置断点后&#xff0c;调试能够在正确的断点处中断&#xff0c;并高亮显示该行二、启动调试步骤一&#xff1a;选择调试设备在设备选择框中&#xff0c;选择调试的设…

2026/10/20 2:21:27 阅读更多 →

最新新闻

从URL取参到Cookie操作:前端开发完整指南与踩坑记录

从URL取参到Cookie操作:前端开发完整指南与踩坑记录

老读者可能还记得&#xff0c;我之前在某公司的运营后台系统里做过一次页面重构&#xff0c;其中有个特别不起眼但又绕不开的需求&#xff1a;从前端拿 URL 上携带的参数&#xff0c;顺手把一些状态写进 Cookie。听起来简单&#xff0c;真正动手的时候才发现&#xff0c;这里面…

2026/10/20 2:54:46 阅读更多 →
不复杂的复杂度:Java开发者必备的大O分析指南

不复杂的复杂度:Java开发者必备的大O分析指南

第八篇了。前面我们用了几篇的篇幅&#xff0c;把Java的语法、面向对象、集合框架、I/O、异常这些基础内容过了一遍。今天想插一个看起来不太一样、但实际非常重要的主题——复杂度分析。为什么在这个节点讲它&#xff1f;因为我发现很多人在前面那些内容学完之后&#xff0c;开…

2026/10/20 2:54:46 阅读更多 →
需求分级与优先级管理:产研团队高效协作的落地指南

需求分级与优先级管理:产研团队高效协作的落地指南

周五下午三点&#xff0c;我正在跟团队过下一迭代的排期。会议室的门被推开&#xff0c;某业务线负责人带着一份PPT走进来&#xff0c;说有一位大客户急需一个定制功能&#xff0c;客户已经在催&#xff0c;下周要看到演示&#xff0c;否则可能影响续约。会议室安静了几秒&…

2026/10/20 2:54:46 阅读更多 →
降AI率工具实测:九类工具原理、风险与一套可复用工作流

降AI率工具实测:九类工具原理、风险与一套可复用工作流

开头部分可以这么写&#xff1a;最近不少本科生来找我看论文初稿&#xff0c;十份里有八份开头都是“随着社会的不断发展”&#xff0c;第二段必是“综上所述”。一问才知道&#xff0c;都是让AI先写一版&#xff0c;自己再改&#xff0c;改完一检测&#xff0c;“AI率”飙到百…

2026/10/20 2:54:46 阅读更多 →
Llama 2 提示词完全指南:模板、参数与实战技巧

Llama 2 提示词完全指南:模板、参数与实战技巧

最近几周我一直在折腾基于 Llama 2 的几个实际项目&#xff0c;从本地部署到 API 接入都走了一遍。反复试下来发现一个很多人容易忽略的事实&#xff1a;Llama 2 和 GPT 系列这类闭源模型的对话习惯差异非常大&#xff0c;直接把原来惯用的提示词搬过去&#xff0c;效果会肉眼可…

2026/10/20 2:54:46 阅读更多 →
大模型Skills应用实战:在Trae中把提示词变成可复用工作流

大模型Skills应用实战:在Trae中把提示词变成可复用工作流

这个系列写到第14篇&#xff0c;我聊点真正能提效的东西——大模型Skills应用。之前几篇要么在讲模型能力边界&#xff0c;要么在讲上下文工程&#xff0c;这次把视角落到一个具体场景&#xff1a;在Trae这款AI原生IDE里&#xff0c;怎么把Skill从"听说过"变成"…

2026/10/20 2:53:45 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →