C语言错误处理:深入解析perror()与strerror()的线程安全与实战应用
1. 项目概述为什么我们需要关注错误信息打印在C语言开发中尤其是涉及系统调用、文件操作、网络通信等底层交互时错误处理是代码健壮性的基石。你肯定遇到过这样的情况程序运行到一半某个文件打不开或者网络连接失败了程序要么悄无声息地崩溃要么只给你一个冷冰冰的“-1”或“NULL”返回值。这时候如果能把“为什么失败”清晰地告诉开发者或者用户调试效率和用户体验将天差地别。perror()和strerror()就是C标准库中专门用来把那个藏在全局变量errno里的错误代码翻译成人类可读的错误描述字符串的两个核心函数。它们看似简单但在实际项目中如何选择和使用却藏着不少门道。用错了不仅调试信息可能不准确甚至可能引入线程安全问题。这篇文章我就结合自己十多年踩坑填坑的经验把这两个函数的里里外外、优缺点对比以及实战中的“潜规则”给你讲透。2. 核心原理与机制errno、perror()和strerror()是如何工作的要理解这两个函数必须先搞懂它们共同的服务对象errno。2.1 错误码的载体全局变量errnoerrno不是一个普通的全局变量。在大多数现代操作系统如Linux、macOS和标准C库实现中它通常被定义为一个宏背后可能是一个线程局部存储TLS的变量。这意味着每个线程都有自己独立的errno副本从而避免了多线程环境下的竞争条件。当一个系统调用或某些标准库函数失败时例如fopen()返回NULLread()返回-1它们会同时将一个代表具体错误原因的数字代码写入当前线程的errno中。这个数字在errno.h头文件中有对应的宏定义比如EACCES权限不足、ENOENT文件或目录不存在、EAGAIN资源暂时不可用等。errno的值只在函数发生错误时被设置并且只有失败时才有效。成功的函数调用不会也不应该将errno清零。因此一个良好的编程习惯是在调用可能设置errno的函数后立即检查其返回值是否指示失败如果是再立刻去读取errno的值因为后续任何成功的库函数调用都可能改变它。2.2 简单的报信员perror()函数perror()函数就像一个急性子的报信员。它的原型是void perror(const char *s);它的工作流程非常直接它首先会打印你传入的字符串s通常用来描述是哪个操作失败了。然后自动加上一个冒号和空格。接着它立即去查找当前errno值对应的错误描述字符串。最后将这个描述字符串打印到标准错误流stderr中并自动换行。它的核心特点是**“即时性”**。它不给你返回字符串而是直接完成“查找-打印”的全过程。这个设计决定了它的优点和局限。2.3 灵活的翻译官strerror()函数strerror()函数则像一个专业的翻译官只负责翻译不负责传达。它的原型是char *strerror(int errnum);它的工作很单纯接受一个错误码errnum通常就是errno然后返回一个指向对应错误描述字符串的指针。它的核心特点是**“灵活性”**。它把描述字符串交还给你你可以用它做任何事情打印到文件、记录到日志系统、拼接进更复杂的错误消息、甚至通过网络发送给客户端。这个设计赋予了它更强大的能力但也带来了需要特别注意的问题。3. 深度对比perror()与strerror()的优缺点剖析了解了原理我们就可以从多个维度对它们进行细致的对比。这个选择不是非黑即白的而是取决于你的具体场景。3.1 输出目标与灵活性perror()缺点输出目标是硬编码的固定为stderr。在守护进程、GUI程序或需要将日志写入特定文件的服务器程序中这很不方便。你不能用perror()直接把错误信息写入syslog或自定义的日志文件。优点对于简单的命令行工具或快速调试直接打印到stderr非常方便无需额外代码指定输出流。strerror()优点极致灵活。你可以将返回的字符串与fprintf()、syslog()、write()等任何输出函数结合输出到任何地方。缺点需要多写一行代码来处理输出对于最简单的场景稍显繁琐。实战心得在开发需要长期运行、有严格日志规范的服务器端程序时我几乎从不使用perror()。我会用strerror(errno)获取描述然后通过日志库如log4c、zlog或自封装函数以特定格式和级别ERROR、WARN等记录到文件或日志系统中。这为后续的日志收集、分析和监控提供了基础。3.2 线程安全性与可重入性这是strerror()的一个历史遗留“坑”也是面试和实际开发中高频的考点。perror()通常是线程安全的。因为它内部直接根据errno线程局部变量查找并输出整个过程不涉及返回静态缓冲区多个线程同时调用perror()不会互相干扰。strerror()在C99标准及之前它不是线程安全的也不是可重入的。问题根源早期实现如glibc的某些版本中strerror()可能会返回一个指向内部静态缓冲区的指针。如果你在多线程环境中调用它一个线程刚拿到指针另一个线程又调用了strerror()内部缓冲区的内容就会被覆盖导致前一个线程拿到的字符串内容突然改变或者打印出混乱的信息。现代解决方案POSIX.1-2001标准引入了线程安全版本的strerror_r()函数。int strerror_r(int errnum, char *buf, size_t buflen); // XSI-compliant version returns int // or char *strerror_r(int errnum, char *buf, size_t buflen); // GNU-specific version returns char*这个函数要求调用者提供一个缓冲区buf和其长度buflen函数将错误字符串写入这个缓冲区从而避免了静态缓冲区的竞争。注意GNU C库提供了一个与标准行为不同的strerror_r()返回char*使用时需注意可移植性问题。为了安全我强烈建议使用strerror_r()并检查其返回值是否为0或ERANGE以确保缓冲区足够大。避坑指南如果你在写多线程程序并且不确定运行环境比如要考虑移植到不同libc最安全的做法是使用strerror_r()。准备一个足够大的栈上缓冲区比如256或512字节。检查strerror_r()的返回值确保转换成功。使用缓冲区中的字符串。 绝对不要在多线程环境下不假思索地使用strerror(errno)。3.3 错误码的适用范围perror()严格绑定于当前的errno。你无法用它来翻译一个非当前的、或自定义的错误码。比如你想记录一个从网络协议中解析出来的远程错误码perror()无能为力。strerror()可以翻译任何传入的整数错误码。虽然标准只保证系统定义的错误码errno.h中的有对应描述对于未知码可能返回“Unknown error”但这个机制本身是开放的。一些库如某些数据库客户端库甚至会扩展strerror()的行为通过修改sys_errlist或类似机制使其能返回库自定义的错误描述。扩展技巧在一些大型项目中我们可能会定义自己的错误码枚举比如MYAPP_ERROR_TIMEOUT 1000。我们可以实现一个类似strerror()的myapp_strerror()函数内部用一个switch或查找表将自定义错误码映射为描述字符串从而在整个应用内提供统一的错误报告接口。3.4 格式化与集成能力perror()格式化能力极弱。你只能加一个前缀字符串。如果想生成“在文件/etc/config.yaml第10行权限不足”这样的复合错误信息perror()无法单独完成。strerror()可以轻松集成到任何格式化输出中。fprintf(logfile, [ERROR][%s] Failed to open file %s: %s (errno%d)\n, timestamp, filename, strerror(errno), errno);这种包含时间戳、模块、错误码和描述、上下文信息的日志条目对于问题定位的价值远超perror()简单的输出。3.5 性能与副作用perror()由于直接输出到stderr而stderr默认通常是无缓冲的每次调用都可能引发一次系统调用如write在频繁出错的高性能场景下虽然这本身是异常情况可能会有轻微性能影响。strerror()/strerror_r()只进行字符串查找和返回或拷贝不涉及I/O操作性能开销更小更可控。小结对比表特性维度perror()strerror()(标准版)strerror_r()(推荐)输出目标固定为stderr由调用者决定由调用者决定线程安全是否是可重入是否是错误码来源必须是当前errno可以是任意整数可以是任意整数格式化能力弱仅加前缀强可任意拼接强可任意拼接适用场景快速原型、简单工具、调试单线程程序或已知线程安全的环境多线程程序、库开发、高可靠性要求场景4. 实战应用与代码示例理论说再多不如代码看一眼。下面我们通过几个典型场景看看如何正确使用它们。4.1 场景一快速调试与简单命令行工具当你写一个一次性脚本、一个简单的命令行工具或者只是想在某个地方快速打印错误时perror()是你的好朋友。它简洁一行搞定。#include stdio.h #include stdlib.h #include errno.h int main() { FILE *fp fopen(/nonexistent/file.txt, r); if (fp NULL) { // 一行代码包含操作上下文和错误描述 perror(fopen failed); // 通常直接退出或进行简单处理 exit(EXIT_FAILURE); } // ... 处理文件 fclose(fp); return 0; }输出可能是fopen failed: No such file or directory注意perror()的参数可以是NULL此时只打印错误描述不打印前缀。但为了可读性强烈建议总是提供一个有意义的上下文前缀比如函数名或操作对象。4.2 场景二单线程应用程序中的错误日志记录在确定的单线程环境如某些嵌入式系统、简单的桌面应用中使用strerror()是安全且灵活的。#include stdio.h #include string.h #include errno.h #include time.h void log_error(const char *operation, const char *target) { time_t now; time(now); char *time_str ctime(now); time_str[strlen(time_str)-1] \0; // 去掉换行符 // 使用strerror因为这是单线程程序 fprintf(stderr, [%s] ERROR: Operation %s on %s failed: %s\n, time_str, operation, target, strerror(errno)); } int main() { if (some_system_call() -1) { log_error(some_system_call, target_resource); } return 0; }4.3 场景三多线程服务器/库开发正确姿势这是体现你工程素养的地方。必须使用strerror_r()。#include stdio.h #include string.h #include errno.h #include pthread.h #define ERR_BUF_SIZE 256 void thread_safe_log_error(int errnum, const char *context) { char err_buf[ERR_BUF_SIZE]; char *err_msg; // 使用GNU版本的strerror_r它返回char* err_msg strerror_r(errnum, err_buf, ERR_BUF_SIZE); // 注意如果使用XSI兼容版本需要判断返回值 // int ret strerror_r(errnum, err_buf, ERR_BUF_SIZE); // if (ret ! 0) { /* 处理错误例如缓冲区不足 */ } // 在实际项目中这里应调用线程安全的日志函数 fprintf(stderr, [Thread %lu] Error in %s: %s\n, (unsigned long)pthread_self(), context, err_msg); } void* worker_thread(void *arg) { // 模拟一个会失败的操作 if (pthread_setname_np(pthread_self(), MyWorker) ! 0) { // 获取当前errno并记录 int saved_errno errno; thread_safe_log_error(saved_errno, pthread_setname_np); } return NULL; }关键点在strerror_r()之前我们先将errno保存到局部变量saved_errno中。这是因为在调用strerror_r()或其他任何函数的过程中errno本身可能会被修改。这是一个非常容易忽略的细节。4.4 场景四处理非标准或未知错误码strerror()对于未知错误码会返回一个通用字符串。为了更好的用户体验我们可以做一层封装。#include stdio.h #include string.h #include errno.h const char* my_strerror(int errnum) { // 先尝试标准解释 static thread_local char buf[512]; // C11后可用_Thread_local此处示意 #ifdef _POSIX_C_SOURCE char tmp_buf[256]; if (strerror_r(errnum, tmp_buf, sizeof(tmp_buf)) 0) { snprintf(buf, sizeof(buf), %s, tmp_buf); } else { snprintf(buf, sizeof(buf), Unknown error (%d), errnum); } #else // 非多线程环境或备用方案 char *msg strerror(errnum); if (msg ! NULL) { snprintf(buf, sizeof(buf), %s, msg); } else { snprintf(buf, sizeof(buf), Unknown error (%d), errnum); } #endif // 这里可以添加对项目自定义错误码的转换 // switch(errnum) { case MY_ERROR: return My custom error; ... } return buf; }这个封装函数提供了更好的兼容性和扩展性是构建健壮错误处理子系统的基础。5. 常见陷阱、疑难杂症与排查实录即使知道了正确用法在实际编码和调试中还是会遇到一些让人头疼的问题。5.1 陷阱一errno被意外覆盖这是最常见的错误之一。// 错误示范 if (write(fd, buf, count) -1) { // 在检查errno之前先调用了另一个可能成功的函数 printf(Write failed. Something else: %d\n, some_other_call()); // 可能修改errno! fprintf(stderr, Error: %s\n, strerror(errno)); // 这里的errno可能已经不是write的错误了 }正确做法在检查到函数失败后立即将errno保存到局部变量。if (write(fd, buf, count) -1) { int saved_errno errno; // 第一时间保存 // ... 可以安全地调用其他函数了 log_error(write, saved_errno); }5.2 陷阱二误用strerror()的返回值不要修改strerror()返回的字符串也不要假设它长期有效。// 危险操作 char *err strerror(errno); strcpy(err, My modified error); // 绝对禁止可能写入只读内存或破坏其他线程的数据。 // 或者 char *err strerror(EACCES); sleep(10); printf(%s\n”, err); // 10秒后err指向的内容可能已被其他线程覆盖正确做法如果需要在后续使用立即将字符串拷贝到自己的缓冲区。char err_buf[256]; snprintf(err_buf, sizeof(err_buf), %s, strerror(errno)); // 现在可以安全地使用err_buf了5.3 陷阱三缓冲区溢出使用strerror_r时strerror_r()要求你提供缓冲区。如果缓冲区太小错误描述会被截断可能丢失关键信息。char tiny_buf[10]; strerror_r(ENOMEM, tiny_buf, 10); // 如果Cannot allocate memory长度超过9字节含结尾\0则会被截断可能引发未定义行为或信息不全。安全建议定义一个足够大的缓冲区。POSIX标准建议至少sysconf(_SC_SYSTEM_STRERROR_R_MAX)字节但更简单实用的做法是使用一个合理的固定大小如256或512字节这能覆盖绝大多数系统的错误信息长度。5.4 疑难杂症如何调试“Unknown error”或乱码有时strerror()会返回“Unknown error”或一串乱码。检查错误码是否有效首先打印errno的值。如果它是一个非常大的、不常见的负数或正数可能不是标准的系统错误码。可能是库自定义的或者errno在函数调用成功后被误读了。检查函数是否真的失败确认调用函数的返回值确实指示了失败。不要因为errno有值就认为是错误成功的调用也可能留下旧的errno值。检查区域设置Locale错误描述字符串可能受LC_MESSAGES环境变量影响。在某些非C或en_US.UTF-8的区域设置下返回的字符串可能是乱码。可以尝试在程序开头调用setlocale(LC_ALL, C)设置为默认C区域。检查内存损坏如果返回的是完全无意义的乱码可能是strerror()内部的静态缓冲区或程序内存发生了损坏这是一个更严重的bug信号。5.5 高级技巧自定义错误码映射在大型项目中除了系统错误还有大量的业务逻辑错误。我们可以构建一个统一的错误处理层。typedef enum { APP_SUCCESS 0, APP_ERROR_INVALID_INPUT 1000, APP_ERROR_NETWORK_TIMEOUT, APP_ERROR_DATABASE_CONN_FAILED, // ... 更多自定义错误 } app_err_code_t; const char* app_strerror(app_err_code_t err) { switch(err) { case APP_SUCCESS: return Success; case APP_ERROR_INVALID_INPUT: return Invalid input parameter; case APP_ERROR_NETWORK_TIMEOUT: return Network operation timed out; case APP_ERROR_DATABASE_CONN_FAILED: return Failed to connect to database; default: { // 对于未知码尝试用系统解释或者直接返回通用信息 if (err 0 err 1000) { // 假设系统错误码在0-999 static __thread char sys_err_buf[256]; strerror_r(err, sys_err_buf, sizeof(sys_err_buf)); return sys_err_buf; } return Unknown application error; } } }这样在整个应用程序中无论是系统错误还是业务错误都可以通过app_strerror()获得统一的、可读的描述极大地提升了错误处理的一致性和可维护性。6. 总结与最佳实践选择经过这么一番拆解选择perror()还是strerror()/strerror_r()答案应该很清晰了。我的个人实践准则是永远优先考虑strerror_r()除非你百分之百确定你的代码永远不会在多线程环境中运行并且对日志输出没有定制化需求否则strerror_r()是更安全、更专业的选择。养成使用它的习惯能避免未来潜在的、难以调试的线程安全问题。perror()仅用于最快速的调试和最简单的单文件小程序当你想在五分钟内写个东西验证想法或者在一个已知的单线程上下文中需要最简短的错误输出时用它没问题。但在任何稍有规模的项目中应避免使用。建立统一的错误处理与日志接口不要在整个代码库中到处直接调用fprintf(stderr, ...)或perror()。封装一个自己的日志函数如log_error(int level, const char *fmt, ...)在这个函数内部安全地使用strerror_r()来整合系统错误信息。这样你可以集中控制日志格式、输出目的地文件、网络、控制台、日志级别和轮转策略。错误信息要包含上下文孤零零的一个“Permission denied”没有太大帮助。错误信息至少应该包含时间戳便于追踪、错误发生的模块或函数、操作的目标对象如文件名、URL、Socket ID、系统错误描述strerror_r的结果和原始错误码。例如[2023-10-27 10:00:00][NETWORK] connect() to 192.168.1.1:80 failed: Connection refused (111)。及时保存errno这值得反复强调。在调用任何可能修改errno的函数包括printf,malloc, 甚至另一个可能失败的系统调用之前如果errno的值对你还有用务必先把它存到局部变量里。错误处理是C语言编程中体现工匠精神的地方之一。看似微不足道的perror()和strerror()选择背后关联着线程安全、代码可维护性、调试效率等一系列工程问题。花点时间把它们理清楚构建起稳健的错误处理框架将来在调试那些深更半夜出现的、难以复现的bug时你会感谢自己当初的这份细致。

相关新闻

工程师必备的误差与精度思维:从概念到实战的工程实践指南

工程师必备的误差与精度思维:从概念到实战的工程实践指南

1. 从“差不多”到“差多少”:一个工程师的精度觉醒干了这么多年技术,从写代码到搞硬件,再到带项目,我越来越觉得,区分一个工程师是“老手”还是“新手”,一个项目是“靠谱”还是“悬乎”,很多时…

2026/10/19 1:00:41 阅读更多 →
1999-2025年各省要素市场错配市场扭曲水平数据+Stata代码

1999-2025年各省要素市场错配市场扭曲水平数据+Stata代码

数据介绍 参考林伯强和杜克锐(2013)的做法进行计算,该值越大,说明地区要素市场错配程度越高。有省级面板数据和用省的数据匹配上市公司两个版本。其中,factor为樊纲等计算的要素市场发育程度指数。樊刚数据使用历年增…

2026/10/17 7:46:53 阅读更多 →
AI Agent原生搜索:让智能助手直接检索原始聊天记录的技术实践

AI Agent原生搜索:让智能助手直接检索原始聊天记录的技术实践

1. 项目概述:当你的AI助手打开聊天记录时最近在折腾AI Agent项目时,我遇到了一个挺有意思的瓶颈:如何让Agent高效地从海量、原始的聊天记录里找到它需要的信息?这听起来像是个简单的搜索问题,但实际操作起来&#xff0…

2026/10/17 7:09:09 阅读更多 →

最新新闻

IC设计私有化AI助手实战:Docker+OpenCode+Ollama数字前端综合增强方案(TaoToken统一接入版)

IC设计私有化AI助手实战:Docker+OpenCode+Ollama数字前端综合增强方案(TaoToken统一接入版)

/* 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 1:00:10 阅读更多 →
地表最强?Gemini 2.5Pro发布后多模型实测对比,TaoToken统一Key接入全流程

地表最强?Gemini 2.5Pro发布后多模型实测对比,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 1:00:10 阅读更多 →
HMM-LSTM融合模型在股票趋势预测中的原理与实战

HMM-LSTM融合模型在股票趋势预测中的原理与实战

简介:本资源是一套面向量化投资与金融时间序列建模学习者的Python实战项目,聚焦股票市场趋势预测这一典型任务,融合隐马尔可夫模型(HMM)与长短期记忆网络(LSTM)的混合建模思想,提供四…

2026/10/19 1:00:10 阅读更多 →
工业时序数据轻量压缩:增量编码与分块位宽自适应实践

工业时序数据轻量压缩:增量编码与分块位宽自适应实践

提到工业数据压缩,很多人的第一反应是上成熟压缩库,或者干脆用通用算法硬压。但真到了现场,你往往会发现另一套逻辑:数据源是一台老式PLC或者传感器采集网关,计算资源有限,实时性要求高,而且数据…

2026/10/19 1:00:10 阅读更多 →
STM32F1与陶晶池USART HMI串口屏:从接线到DMA多指令刷屏实战

STM32F1与陶晶池USART HMI串口屏:从接线到DMA多指令刷屏实战

简介:面向嵌入式开发者的陶晶池USART HMI串口屏与STM32F1通信详解资源包,聚焦STM32F103与串口屏的异步串行通信实现。内容覆盖USART波特率计算与USART_BRR寄存器配置、数据帧格式设定、TXE/RXNE标志处理及中断收发机制,并附有完整通信程序示例…

2026/10/19 0:59:10 阅读更多 →
Postman接口测试从入门到自动化实战指南

Postman接口测试从入门到自动化实战指南

1. 准备工作:安装、界面与第一个请求1.1 为什么是Postman,而不是其他工具先聊点实际的。我在测试岗摸爬滚手这几年,见过不少团队在接口测试工具上的纠结史:有人用命令行curl一把梭,有人用浏览器开发者工具手动拼请求&a…

2026/10/19 0:58:09 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →