C语言指针函数详解:安全返回指针的三种模式与内存管理实践
1. 从“返回指针的函数”说起一个看似简单却暗藏玄机的概念在C语言的世界里指针和函数是两大基石而当它们结合在一起时总能创造出一些既强大又容易让人栽跟头的特性。今天要聊的“返回指针值的函数”很多教材上称之为“指针函数”听起来是不是有点绕其实拆开看很简单一个函数它的返回值类型是一个指针。比如int* func()这个func函数执行完毕后不是返回一个整数而是返回一个指向整数的内存地址。这个概念本身不复杂但为什么它值得单独拿出来用一整篇的篇幅来“详解”因为这里面的坑远比函数本身声明要深得多。我见过太多新手甚至是有些经验的开发者在这里翻车。最常见的错误就是返回了一个指向局部变量的指针函数一结束那块内存就被回收了你拿到的指针就成了一个指向“垃圾数据”或引发程序崩溃的“野指针”。所以理解这个特性关键不在于语法而在于对C语言内存管理生命周期深刻的理解。它直接考验你对栈内存、堆内存、静态存储区这些核心概念的掌握程度。掌握了它你就能写出更灵活、更高效的代码比如动态创建数据结构并返回其引用没掌握好你的程序就会变得脆弱不堪bug难以追踪。2. 语法、声明与本质如何正确地定义一个指针函数首先我们得把语法搞明白避免一些低级错误。一个返回指针的函数的声明格式如下返回类型 * 函数名(参数列表);注意*是紧挨着函数名的它表示函数的返回值是一个指针。这里有个经典的、让初学者困惑的“优先级”问题。对比一下这两个声明int *func();// 这是一个函数名为func它返回一个指向int的指针。int (*func)();// 这是一个指针名为func它指向一个返回int的函数这叫函数指针。看清楚区别了吗第一个的*修饰的是func()这个整体说明func这个函数的返回值类型是int*。第二个的*被括号括起来修饰的是func本身说明func是一个指针。为了避免歧义我个人的习惯是采用第一种写法但将*紧贴返回类型写成int* func();这样在视觉上更清晰地表明int*是一个整体是返回类型。当然编译器都能识别。让我们看一个最简单的合法例子#include stdio.h int* get_pointer_to_static() { static int value 42; // 静态局部变量生命周期是整个程序 return value; // 返回它的地址是安全的 } int main() { int* ptr get_pointer_to_static(); printf(The value is: %d\n, *ptr); // 输出 The value is: 42 return 0; }这个例子是安全的因为value是static的它不在栈上它的内存在程序启动时就被分配直到程序结束才释放。所以函数返回后这个地址依然有效。3. 内存管理的雷区为什么不能返回局部变量的地址现在我们来直面那个最经典的陷阱。我敢打赌每个C程序员早期都至少写过一次下面这样的代码#include stdio.h int* create_bad_pointer() { int local_var 100; return local_var; // 致命错误返回了局部变量的地址 } int main() { int* dangerous_ptr create_bad_pointer(); printf(The value is: %d\n, *dangerous_ptr); // 未定义行为 return 0; }这段代码可能在某些环境下、某次运行时“碰巧”能打印出100但这完全是靠运气。一旦你稍微修改程序或者开启编译优化结果就会变得诡异或者直接导致程序崩溃。为什么这涉及到函数调用栈的工作原理。当create_bad_pointer函数被调用时系统会在栈上为它的局部变量local_var分配内存。函数执行结束时它的栈帧stack frame就被销毁更准确地说是这块栈内存可以被后续的函数调用覆盖。此时local_var这个地址虽然还是一个数值但它指向的内存区域已经“失效”了不再属于你的程序逻辑上该使用的空间。后续任何操作无论是main函数还是其他函数只要使用了这块栈内存就会覆盖掉原来local_var存储的100。你通过dangerous_ptr去读取读到的就是不可预测的“垃圾值”。这是一种“未定义行为”Undefined Behavior, UB。在C语言中未定义行为意味着任何事情都可能发生程序可能崩溃可能输出错误结果也可能在某些特定环境下“正常”工作。这是最危险的bug类型因为它具有隐蔽性和不确定性。所以铁律一永远不要返回指向局部自动变量非static的指针。那么哪些指针是可以安全返回的呢主要有三类指向静态存储期变量的指针就像上面例子中的static int value。静态变量包括全局变量的生命周期是整个程序运行期。指向动态分配内存的指针使用malloc、calloc、realloc等在堆heap上分配的内存。堆内存的生命周期由程序员手动控制直到调用free释放。传入的指针参数如果函数接收一个指针作为参数对这个指针指向的内存进行操作后再将这个指针或基于它计算出的另一个指针返回通常是安全的因为内存的所有权在调用者那里。4. 安全返回指针的三种核心模式与实践理解了禁区我们来看看正确且常用的姿势。掌握这三种模式你就能应对绝大多数需要返回指针的场景。4.1 模式一返回指向静态/全局变量的指针这种模式最简单适用于返回一些固定的、程序生命周期内不变或可重用的数据。优点实现简单无需担心内存释放程序结束自动清理。缺点非线程安全如果多个线程同时调用这个函数并修改它返回的指针所指向的数据会导致数据竞争。不可重入函数内部状态依赖于静态变量多次调用可能会相互干扰。调用者无法控制内存返回的指针指向同一块内存如果调用者修改了内容会影响所有持有该指针的地方。典型应用场景错误信息字符串、固定的查找表Look-up Table、单例模式简单实现等。const char* get_error_message(int error_code) { static const char* messages[] { Success, File not found, Permission denied, Out of memory }; if (error_code 0 error_code sizeof(messages)/sizeof(messages[0])) { return messages[error_code]; } return Unknown error; }4.2 模式二返回指向动态分配内存堆内存的指针这是最强大、最灵活也是最需要谨慎使用的模式。函数内部使用malloc等分配内存将地址返回给调用者。优点灵活性高可以在运行时决定分配内存的大小和内容。生命周期明确内存由调用者负责管理free控制权清晰。线程安全潜力每次调用分配新内存数据相互独立。缺点必须手动管理内存调用者必须记住在适当的时候free掉返回的指针否则会导致内存泄漏。所有权转移函数将内存的所有权“移交”给了调用者这需要在文档中明确说明否则容易造成混淆或重复释放。这是最需要建立“契约”的模式。一个良好的习惯是为这类函数起一个能体现其分配行为的名字比如create_xxx(),allocate_xxx(),new_xxx()。#include stdlib.h #include string.h // 契约调用者负责释放返回的字符串内存 char* create_greeting(const char* name) { // 计算所需内存字符串长度 结尾空字符 额外文本 int len strlen(name) strlen(Hello, !) 1; char* greeting (char*)malloc(len * sizeof(char)); if (greeting NULL) { return NULL; // 分配失败返回空指针 } sprintf(greeting, Hello, %s!, name); return greeting; // 返回堆内存地址 } int main() { char* msg create_greeting(World); if (msg ! NULL) { printf(%s\n, msg); free(msg); // 契约履行使用完毕后释放内存 msg NULL; // 良好习惯避免悬空指针 } return 0; }一个关键细节注意检查malloc的返回值是否为NULL。在内存不足时malloc会返回NULL你的函数应该将这种错误状态传递出去而不是直接解引用。4.3 模式三返回传入的指针参数或基于其的偏移这种模式通常用于“处理器”或“工具”函数。函数接收一个指针对其进行操作如填充数据、移动位置然后返回它或移动后的新指针。内存的所有权始终在调用者手中。优点内存管理责任清晰调用者分配调用者释放函数只负责使用。高效避免了在函数内部进行额外的动态分配。支持链式调用返回值可以立即作为另一个函数的参数。缺点要求调用者提前分配好足够的内存并传入。函数需要处理传入指针可能为NULL的情况。// 将一个字符串转换为大写并返回指向同一字符串的指针便于链式调用 char* to_uppercase(char* str) { if (str NULL) return NULL; char* p str; while (*p) { if (*p a *p z) { *p *p - (a - A); } p; } return str; } // 在字符串中查找字符返回指向第一次出现位置的指针未找到返回NULL char* find_char(char* str, char ch) { if (str NULL) return NULL; while (*str ! \0) { if (*str ch) { return str; // 返回的是传入字符串内部的某个地址 } str; } return NULL; } int main() { char buffer[100] hello, world; printf(%s\n, to_uppercase(buffer)); // 输出HELLO, WORLD // 链式调用先转大写再查找逗号 char* comma_pos find_char(to_uppercase(buffer), ,); // ... }5. 进阶返回指向结构体、数组与函数的指针当返回的指针指向更复杂的类型时原理不变但有一些细节需要注意。5.1 返回指向结构体的指针这在需要创建和返回复杂数据结构时非常有用例如链表节点、树节点等。typedef struct { int x; int y; } Point; // 模式二动态创建Point Point* create_point(int x, int y) { Point* p (Point*)malloc(sizeof(Point)); if (p ! NULL) { p-x x; p-y y; } return p; } // 模式三初始化已分配的Point Point* init_point(Point* p, int x, int y) { if (p ! NULL) { p-x x; p-y y; } return p; } int main() { // 使用动态创建 Point* p1 create_point(10, 20); if (p1) { printf(Point: (%d, %d)\n, p1-x, p1-y); free(p1); } // 使用栈上分配然后初始化 Point p2; init_point(p2, 30, 40); printf(Point: (%d, %d)\n, p2.x, p2.y); // p2在栈上无需free }注意当结构体内部包含指针成员如字符串时情况会变得更复杂。动态创建的结构体其内部的指针成员可能也需要动态分配这就涉及“深拷贝”与“浅拷贝”的问题以及如何设计一个配套的“销毁函数”来正确释放所有内存。5.2 返回指向数组的指针或数组指针这里容易混淆。函数不能直接返回一个“栈上的数组”原因和不能返回局部变量指针一样。但我们可以返回一个指向数组首元素的指针或者返回一个指向整个数组的指针数组指针。返回指向数组首元素的指针这实际上就是返回一个T*例如int*调用者可以像使用数组一样使用它。这通常需要结合动态内存分配模式二。// 创建一个长度为len的整数数组 int* create_int_array(size_t len) { // calloc会初始化为0malloc不初始化 int* arr (int*)calloc(len, sizeof(int)); return arr; // 返回的是 int*可当作数组使用 }返回数组指针语法稍微古怪一些用于明确表示返回的是一个指向固定长度数组的指针。// 定义一个返回指向“含有5个int的数组”的指针的函数 int (*create_matrix_row())[5] { static int row[5] {1, 2, 3, 4, 5}; // 必须是静态或全局 return row; // 返回整个数组的地址 } int main() { int (*row_ptr)[5] create_matrix_row(); // 使用 (*row_ptr)[i] 或 row_ptr[0][i] 来访问元素 for (int i 0; i 5; i) { printf(%d , (*row_ptr)[i]); } }这种写法在实战中较少见更多出现在库的底层实现或对类型有严格要求的场景。5.3 返回函数指针这是C语言中更高级的特性允许函数返回另一个函数的地址。这在实现回调机制、策略模式、函数表Dispatch Table时非常有用。#include stdio.h // 定义两种操作函数类型 typedef int (*Operation)(int, int); int add(int a, int b) { return a b; } int subtract(int a, int b) { return a - b; } // 根据操作符返回对应的函数指针 Operation get_operation(char op) { switch (op) { case : return add; case -: return subtract; default: return NULL; } } int main() { int x 10, y 5; Operation op_func get_operation(); if (op_func ! NULL) { int result op_func(x, y); // 等价于 add(10, 5) printf(Result: %d\n, result); // 输出15 } return 0; }理解函数指针的关键是typedef。它把复杂的函数指针类型声明简化成一个别名如Operation使得代码可读性大大增强。6. 实战中的陷阱、调试技巧与最佳实践理论说再多不如踩一次坑记得牢。下面分享几个我实际开发中总结的经验和教训。6.1 野指针与内存泄漏的排查问题场景程序运行一段时间后崩溃或者内存占用不断增长内存泄漏。排查思路审查所有返回指针的函数重点关注那些返回malloc分配内存的函数。确认每个这样的函数其调用者是否都有配对的free操作。一个简单的准则是malloc或calloc的次数必须等于free的次数。使用工具在Linux/Unix下valgrind是你的最佳伙伴。用valgrind --leak-checkfull ./your_program运行程序它能精确指出内存泄漏的位置和大小以及非法内存访问如使用已释放内存。防御性编程在free指针后立即将其设为NULL。这样即使后续不小心再次访问或free对NULL指针的操作通常是安全的free(NULL)是空操作。明确所有权在函数注释或文档中明确说明返回的指针的内存所有权。例如“调用者负责释放返回的字符串。” 或 “返回指向内部静态缓冲区的指针不要试图释放它。”6.2 多线程环境下的灾难问题场景一个返回静态指针的函数在多线程程序中被并发调用导致数据错乱或崩溃。// 非线程安全版本 char* timestamp_string() { static char buffer[64]; time_t now time(NULL); ctime_r(now, buffer); // 即使使用ctime_rbuffer本身是静态的多个线程会覆盖 buffer[strlen(buffer)-1] \0; // 去掉换行符 return buffer; } // 线程A调用拿到了buffer地址。此时线程B也调用修改了buffer内容。线程A再使用拿到的指针内容已经变了解决方案方案A线程局部存储使用_Thread_local(C11) 或__thread(GCC扩展) 关键字让每个线程拥有自己的buffer副本。_Thread_local static char buffer[64];方案B由调用者提供缓冲区改为模式三让调用者传入缓冲区地址和大小。这是最安全、最推荐的做法。char* timestamp_string(char* buffer, size_t buf_size) { time_t now time(NULL); struct tm tm_info; localtime_r(now, tm_info); strftime(buffer, buf_size, %Y-%m-%d %H:%M:%S, tm_info); return buffer; }方案C返回动态内存改为模式二每次调用都返回新分配的内存。但务必记得释放且性能有开销。6.3 常量正确性Const Correctness这是一个提升代码健壮性和可读性的重要实践。如果函数返回的指针指向的内容不应该被修改请务必给返回类型加上const修饰符。// 好明确告诉调用者你不能修改返回字符串的内容 const char* get_readonly_config() { static const char* config Some settings; return config; } // 调用者尝试修改会导致编译错误 // char* ptr get_readonly_config(); // 错误丢弃了const限定符 // ptr[0] X; // 编译错误如果强制转换了指针则是运行时未定义行为 const char* ptr get_readonly_config(); // 正确 // ptr[0] X; // 编译错误不允许修改这利用了编译器的类型检查来防止意外修改是防御性编程的重要手段。6.4 错误处理与空指针返回一个健壮的指针返回函数必须考虑失败情况。对于动态分配内存的函数分配失败时应返回NULL。调用者必须检查返回值。int* allocate_large_array(size_t count) { if (count SIZE_MAX / sizeof(int)) { // 防止溢出 return NULL; } int* arr (int*)malloc(count * sizeof(int)); if (arr NULL) { // 可以在这里记录日志但不要调用exit()把决定权交给调用者 return NULL; } return arr; } int main() { int* data allocate_large_array(1000000000ULL); // 很大的数 if (data NULL) { fprintf(stderr, Memory allocation failed. Exiting gracefully.\n); // 进行清理然后退出或尝试恢复 return EXIT_FAILURE; } // ... 使用 data free(data); }7. 综合案例构建一个简单的字符串工具库让我们把上面的知识融会贯通设计几个简单的字符串工具函数看看在实际项目中如何应用这些模式。// string_utils.h #ifndef STRING_UTILS_H #define STRING_UTILS_H #include stddef.h // for size_t /* 模式二动态创建调用者负责free */ // 将字符串str重复count次返回新的字符串 char* string_repeat(const char* str, size_t count); /* 模式三操作调用者提供的缓冲区 */ // 将字符串转换为小写结果存入dst需足够大返回dst char* string_to_lower(char* dst, const char* src); // 安全的字符串连接防止缓冲区溢出返回dst char* string_concat_safe(char* dst, size_t dst_size, const char* src); /* 模式一/三结合返回静态缓冲区但提供线程安全版本 */ // 非线程安全返回静态缓冲区指针 const char* get_formatted_date_unsafe(); // 线程安全使用调用者提供的缓冲区 char* get_formatted_date_safe(char* buffer, size_t size); #endif// string_utils.c #include string_utils.h #include stdlib.h #include string.h #include time.h #include ctype.h char* string_repeat(const char* str, size_t count) { if (str NULL || count 0) { char* empty (char*)malloc(1); if (empty) empty[0] \0; return empty; } size_t str_len strlen(str); // 检查乘法溢出 if (count SIZE_MAX / (str_len 1)) { return NULL; } size_t total_len str_len * count; char* result (char*)malloc(total_len 1); // 1 for null terminator if (result NULL) { return NULL; } char* p result; for (size_t i 0; i count; i) { memcpy(p, str, str_len); p str_len; } *p \0; return result; } char* string_to_lower(char* dst, const char* src) { if (dst NULL || src NULL) { return dst; } char* d dst; while ((*d tolower((unsigned char)*src))) { // 循环体为空所有操作在条件中完成 } return dst; } char* string_concat_safe(char* dst, size_t dst_size, const char* src) { if (dst NULL || src NULL || dst_size 0) { return dst; } size_t dst_len strnlen(dst, dst_size); if (dst_len dst_size) { // dst已经满了或者没有空字符直接返回 return dst; } size_t src_len strnlen(src, dst_size - dst_len - 1); if (src_len 0) { memcpy(dst dst_len, src, src_len); dst[dst_len src_len] \0; } return dst; } // 非线程安全版本 const char* get_formatted_date_unsafe() { static char buffer[64]; time_t now time(NULL); struct tm* tm_info localtime(now); // 注意localtime不是线程安全的 strftime(buffer, sizeof(buffer), %Y-%m-%d %H:%M:%S, tm_info); return buffer; } // 线程安全版本 char* get_formatted_date_safe(char* buffer, size_t size) { if (buffer NULL || size 1) { return NULL; } time_t now time(NULL); struct tm tm_info; localtime_r(now, tm_info); // 使用可重入版本 strftime(buffer, size, %Y-%m-%d %H:%M:%S, tm_info); return buffer; }这个案例展示了不同模式的混合使用string_repeat采用模式二动态分配内存调用者必须free。string_to_lower和string_concat_safe采用模式三操作调用者的缓冲区避免了内部分配更高效、更安全。get_formatted_date提供了两个版本清晰地展示了线程安全与非线程安全的区别以及模式一和模式三的对比。在实际项目中应优先使用线程安全的、由调用者提供缓冲区的版本。指针函数是C语言赋予程序员强大控制力的工具之一。它像一把双刃剑用好了可以优雅地构建复杂系统用不好则会引入难以追踪的bug。核心始终是理解内存的生命周期。在编写任何一个返回指针的函数时都要下意识地问自己我返回的这个地址它所指向的内存在函数返回后是否依然有效谁拥有它的所有权谁负责释放它把这些问题想清楚并通过清晰的函数命名和文档传达出去你的代码质量会提升一个档次。最后善用valgrind等工具进行验证将静态分析编译器警告和动态检查结合起来才能让指针在手中游刃有余而不是如履薄冰。

相关新闻

Web端PvP实时对战开发:从状态同步到伤害计算的实战指南

Web端PvP实时对战开发:从状态同步到伤害计算的实战指南

1. 从零到一:为什么Web端PvP实时对战是个“技术活”?最近几年,Web端游戏,特别是实时对战类游戏,热度越来越高。你可能在浏览器里玩过一些“即点即玩”的射击、IO类游戏,感觉流畅度和原生App相差无几。但作为…

2026/10/13 18:06:15 阅读更多 →
C++17 std::visit 深度解析:从访问者模式到高效类型安全分发

C++17 std::visit 深度解析:从访问者模式到高效类型安全分发

1. 从模式匹配到访问者模式:为什么我们需要 std::visit?如果你写过一段时间 C,尤其是处理过像std::variant这样的类型安全联合体,你大概率会怀念其他语言里的模式匹配(Pattern Matching)。想象一下&#xf…

2026/9/27 23:23:08 阅读更多 →
Redis最大连接数配置与调优:从原理到实战的完整指南

Redis最大连接数配置与调优:从原理到实战的完整指南

1. 项目概述:从一次线上告警说起 那天凌晨,手机突然被一阵急促的告警信息震醒。监控大屏上,核心业务服务的响应时间曲线像坐了火箭一样直线飙升,紧接着就是一连串的“Redis连接超时”错误。团队被紧急拉起来排查,一通操…

2026/10/11 3:23:08 阅读更多 →

最新新闻

无支撑金属3D打印技术如何革新卫星制造

无支撑金属3D打印技术如何革新卫星制造

1. 项目概述:当太空基建遇上金属增材制造去年参观某航天器制造车间时,车间主任指着角落里一台正在工作的金属3D打印机说:"这玩意儿现在打的都是非承力件,要是能直接打印主结构件,我们的卫星产能至少翻三倍。"…

2026/10/15 9:51:48 阅读更多 →
Moodle部署到教学管理:从环境配置到避坑的完整实战指南

Moodle部署到教学管理:从环境配置到避坑的完整实战指南

简介:一份Moodle在线学习平台的使用说明文档,面向需要快速掌握课程资源建设与教学活动的教师、培训师及平台管理员,帮助解决在Moodle中创建和管理在线课程内容的核心问题。资源为单个doc文件,整体压缩包约9.02MB,内容编…

2026/10/15 9:51:48 阅读更多 →
HDS VSP Gx00存储安装配置指南:四个关键环节实操解析

HDS VSP Gx00存储安装配置指南:四个关键环节实操解析

简介:HDS VSP G200安装配置指南是一份面向存储管理员与系统工程师的实操型手册,聚焦Panama系列G200/G400/G600/G800统一搭载的SVOS系统,适合在设备开箱上架、控制框连线、SVP管理环境搭建等阶段按图索骥。资源包体积7.01MB,仅含1个…

2026/10/15 9:51:48 阅读更多 →
深信服翔鹰计划面试本质:安全产品工程师的能力建模

深信服翔鹰计划面试本质:安全产品工程师的能力建模

1. 这不是一场普通的技术面试:翔鹰计划背后的选拔逻辑“深信服翔鹰计划技术面试”——这八个字在应届生和初级工程师圈子里,近两三年逐渐从模糊的招聘词条,变成一个带着具体压力感的实操场景。我接触过不少参与过该流程的某高校学生和某实验室…

2026/10/15 9:51:48 阅读更多 →
Kettle循环批量同步Oracle到Elasticsearch实战:批次、循环与Job编排

Kettle循环批量同步Oracle到Elasticsearch实战:批次、循环与Job编排

1. 从Oracle到Elasticsearch:为什么批量同步这件事值得认真对待做过数据同步的人都有一个共识:把数据从关系型数据库搬到搜索引擎,听起来只是"读出来、写进去"两步,但真正落地的时候,坑几乎全在细节里。尤其…

2026/10/15 9:51:48 阅读更多 →
ASP.NET Core Cookie身份验证实现与安全配置指南

ASP.NET Core Cookie身份验证实现与安全配置指南

1. 身份验证基础与Cookie机制解析现代Web应用的身份验证主要有三种主流方式:Cookie认证、JWT令牌和OAuth协议。其中Cookie认证因其简单易用、浏览器原生支持的特性,成为ASP.NET Core中最常用的身份验证方案之一。Cookie本质上是一小段存储在客户端的文本…

2026/10/15 9:50:47 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →