C语言printf()函数深度解析:从格式控制到调试实战
1. 项目概述为什么printf()值得你花时间深究在C语言的世界里printf()函数就像空气和水一样无处不在却又常常被我们习以为常地忽略。很多初学者甚至是有一定经验的开发者都把它简单地看作一个“打印输出”的工具敲下printf(“Hello, World\n”);就算入门了。但如果你真的这么想那可能错过了C语言中一个极其强大且精妙的设计。我见过太多人在调试时对着屏幕上莫名其妙的输出抓耳挠腮或者在处理浮点数精度、内存地址对齐时栽了跟头其根源往往是对printf()的格式控制符一知半解。这个函数远不止是输出文本。它是你窥探程序内部状态的窗口是格式化数据的瑞士军刀更是理解C语言“格式字符串”这一独特概念的绝佳入口。从控制整数输出的宽度和对齐到精确指定浮点数的小数位数从打印内存地址指针到处理宽字符printf()的格式字符串format string本身就是一门微型的领域特定语言。掌握它意味着你能更清晰、更准确、更高效地输出调试信息构建用户界面甚至在某些场景下它与安全漏洞如格式化字符串漏洞也紧密相关。无论你是正在啃翁恺老师C语言练习题的学生还是在VSCode里配置环境的新手亦或是被“printf输出小数点后6位的逻辑”所困扰的探索者深入理解printf()都将让你的C语言功力提升一个明显的台阶。2. printf() 核心机制与格式字符串深度解析2.1 函数原型与基本工作流程要理解printf()首先得看它的“身份证”——函数原型。在标准头文件stdio.h中它的声明如下int printf(const char *format, ...);这个原型透露了三个关键信息返回值int它返回成功输出的字符总数。如果发生输出错误则返回一个负值。这个返回值常被忽略但在需要精确控制输出流或错误处理时很有用。核心参数const char *format这是一个指向常量字符数组即字符串的指针也就是我们所说的“格式字符串”。它是printf()的灵魂决定了输出的最终形态。可变参数...这表示printf()可以接受数量可变的后续参数。这些参数的值将按照格式字符串中的“转换说明符”所指定的类型和顺序被读取、转换并输出。它的工作流程可以概括为解析格式字符串逐字符输出遇到%开头的转换说明符时从可变参数列表中按顺序取出对应参数按照说明符的规则进行格式化然后将结果插入到输出流中。2.2 格式字符串的完整语法拆解格式字符串中普通字符如 “Hello, “会原样输出。真正的魔法始于百分号%。一个完整的转换说明符的语法如下其中[ ]内的部分是可选的%[flags][width][.precision][length]type让我们像拆解精密仪器一样逐个分析每个部件。2.2.1 类型说明符 (type)决定数据如何解释这是唯一必须的部分它告诉printf()“我后面给你的那个参数请你当成什么类型的数据来处理”说明符对应参数类型输出格式典型示例与输出d,iint有符号十进制整数printf(“%d”, 42);-42uunsigned int无符号十进制整数printf(“%u”, -1);(在32位系统) -4294967295ounsigned int无符号八进制整数printf(“%o”, 10);-12x,Xunsigned int无符号十六进制整数x小写X大写printf(“%x”, 255);-fff,Fdouble十进制小数形式浮点数printf(“%f”, 3.14159);-3.141590e,Edouble科学计数法e小写E大写printf(“%e”, 1000.0);-1.000000e03g,Gdouble自动选择%f或%e中更紧凑的形式printf(“%g”, 0.0000123);-1.23e-05cint(转换为unsigned char)单个字符printf(“%c”, 65);-Aschar *字符串直到遇到空字符\0printf(“%s”, “Hello”);-Hellopvoid *指针地址通常以十六进制输出int a; printf(“%p”, a);-0x7ffeebd2a8cc%无输出一个百分号字符本身printf(“%%”);-%注意这里有一个经典误区。printf(“%f”, num);中的num如果是float类型在传递给printf()的可变参数时会自动提升为double类型。这是因为C语言的可变参数函数中float类型参数会被提升为doublechar/short会被提升为int。所以%f始终期待一个double类型的参数。如果你传了一个float它在被读取前就已经变成了double因此不会出错。但反过来用%lf来输出double在C99标准后也是允许的l长度修饰符但本质上%f和%lf对于printf()是一样的。2.2.2 标志 (flags)控制对齐、前缀和填充标志字符紧跟在%后面用于微调输出的外观。标志含义示例设num42width6-左对齐。默认是右对齐。printf(“总是显示正负号或-。printf(“%d”, num);-42(空格)正数前留空负数前显示-。printf(“% d”, num);-42#替代形式。对o增加前导0对x/X增加前导0x/0X对f/e/g强制输出小数点。printf(“%#x”, num);-0x2a0用前导零填充宽度而非默认的空格。如果指定了-标志或.precision对于整数则0标志被忽略。printf(“%06d”, num);-0000422.2.3 域宽 (width) 与精度 (.precision)控制输出的尺寸域宽 (width)一个十进制数字字符串指定了该字段输出的最小字符数。如果转换后的值字符数少于宽度则用填充字符默认空格0标志下为零填充。如果转换结果更长则宽度限制失效按实际输出。宽度可以是固定值也可以用*指定此时宽度值由下一个参数必须是int类型提供。int width 8; printf(“%*d”, width, 100); // 等价于 printf(“%8d”, 100);精度 (.precision)一个点.后跟的十进制数字字符串。对于不同的类型含义不同对于整数类型 (d, i, o, u, x, X)精度指定了输出的最小数字位数。如果数字位数不足用前导零填充超过则无影响。精度为0且参数为0时输出为空。printf(“%.4d”, 12); // 输出 0012 printf(“%.0d”, 0); // 输出为空无字符对于浮点类型 (f, e, E)精度指定了小数点后显示的位数。默认精度是6这也是为什么printf(“%f”, 3.14);会输出3.140000。printf(“%.2f”, 3.14159); // 输出 3.14对于g或G精度指定了最大有效数字位数。对于字符串 (s)精度指定了最多输出的字符数。超出的部分会被截断。printf(“%.5s”, “Hello, World”); // 输出 Hello精度也可以用*指定由下一个参数提供。int prec 3; printf(“%.*f”, prec, 3.14159); // 输出 3.142 (注意四舍五入)2.2.4 长度修饰符 (length)指定参数的确切大小长度修饰符告诉printf()可变参数列表中的参数是哪种“尺寸”的整数或浮点数。这是避免未定义行为的关键尤其是在处理short,long,long long类型时。修饰符适用类型说明符对应参数类型hhd,i,o,u,x,Xsigned char或unsigned charhd,i,o,u,x,Xshort int或unsigned short intld,i,o,u,x,Xlong int或unsigned long intlld,i,o,u,x,Xlong long int或unsigned long long intLf,e,E,g,Glong double常见坑点sizeof(long)在不同平台如Windows的MSVC和Linux的GCC可能不同。在64位Linux上long通常是8字节而在64位Windows上long通常是4字节。因此打印long类型变量时使用%ld是安全的但要注意其表示范围可能因平台而异。对于固定大小的整数如int32_t更推荐使用inttypes.h中定义的宏如PRId32。3. 核心应用场景与实战技巧3.1 精准控制数值输出以“银行系统小数点精度”为例金融计算中金额输出必须精确到分且格式统一。假设我们要输出一个金额double amount 1234.567;要求输出为1,234.57四舍五入到分千位分隔符。标准C库的printf()不直接支持千位分隔符但我们可以通过组合标志、宽度和精度实现大部分要求。#include stdio.h #include math.h // 用于round函数 int main() { double amount 1234.567; // 目标1,234.57 // 1. 四舍五入到小数点后两位 double rounded_amount round(amount * 100.0) / 100.0; // 2. 使用宽度和精度控制输出 // %10.2f总宽度10位小数点后2位右对齐 printf(“金额: %10.2f\n”, rounded_amount); // 输出金额: 1234.57 // 3. 如果想左对齐并填充* printf(“金额: %-10.2f元\n”, rounded_amount); // 输出金额: 1234.57 元 // 更复杂的千位分隔符需要手动处理字符串这里展示精度控制的核心 double a 0.1; double b 0.2; double sum a b; printf(“a b %.20f\n”, sum); // 提高精度查看浮点误差 // 输出可能为a b 0.30000000000000004441 // 因此在比较浮点数时不应直接使用 而应判断两者差的绝对值是否小于一个极小值如1e-9。 return 0; }实操心得对于严格的金融计算double的二进制浮点表示仍可能存在微小误差。在要求绝对精确的场景如涉及法律计算的利息应考虑使用定点数库或直接以分为单位用long long整数存储和计算输出时再格式化为元角分。3.2 调试利器指针、内存与数据探查printf()是C语言调试中最直观的工具之一远超简单的Hello, World。3.2.1 查看变量地址与内存布局int arr[5] {1, 2, 3, 4, 5}; char str[] “Hello”; printf(“数组arr的起始地址: %p\n”, (void*)arr); printf(“数组arr[0]的地址: %p\n”, (void*)arr[0]); printf(“数组arr[1]的地址: %p\n”, (void*)arr[1]); // 可以观察地址差值验证数组元素的连续性 printf(“地址差值字节: %td\n”, (char*)arr[1] - (char*)arr[0]); printf(“字符串str的地址: %p\n”, (void*)str); printf(“字符串内容: %s\n”, str); // 注意%s期待的是指向字符数组以‘\0’结尾的指针。3.2.2 以十六进制查看内存内容这在分析二进制数据、网络包或排查内存损坏时非常有用。int value 0xDEADBEEF; unsigned char *p (unsigned char*)value; printf(“整数值 %d 的十六进制: 0x%08x\n”, value, value); printf(“按字节查看内存小端序为例:\n”); for (int i 0; i sizeof(value); i) { printf(“地址 %p: 0x%02x\n”, (void*)(p i), p[i]); } // 输出可能在小端序机器上 // 整数值 -559038737 的十六进制: 0xdeadbeef // 按字节查看内存: // 地址 0x7ffeed3aabcc: 0xef // 地址 0x7ffeed3aabcd: 0xbe // 地址 0x7ffeed3aabce: 0xad // 地址 0x7ffeed3aabcf: 0xde3.2.3 格式化字符串漏洞的简单认知虽然这是一个安全话题但了解其原理有助于你理解printf()的工作原理。printf()信任格式字符串并按照其中的说明符去栈上寻找参数。如果用户控制了格式字符串就可能发生危险。// 错误示例永远不要这样做 char user_input[100]; scanf(“%s”, user_input); // 假设用户输入了 “%x %x %x” printf(user_input); // 危险这等价于 printf(“%x %x %x”);上面的代码会直接打印栈上的内容可能导致信息泄露。正确的做法是始终使用固定的格式字符串printf(“%s”, user_input);。3.3 构建格式化日志系统一个健壮的日志系统需要时间戳、日志级别和格式化消息。我们可以封装printf()。#include stdio.h #include time.h #include stdarg.h #define LOG_INFO(fmt, ...) log_print(“INFO”, __FILE__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) log_print(“ERROR”, __FILE__, __LINE__, fmt, ##__VA_ARGS__) void log_print(const char *level, const char *file, int line, const char *fmt, …) { time_t now; time(now); struct tm *local localtime(now); char time_buf[20]; strftime(time_buf, sizeof(time_buf), “%Y-%m-%d %H:%M:%S”, local); // 打印固定的头部信息 fprintf(stderr, “[%s] [%s] %s:%d - “, time_buf, level, file, line); // 处理可变参数打印用户的自定义消息 va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, “\n”); } int main() { int ret -1; LOG_INFO(“程序启动成功。”); if (ret 0) { LOG_ERROR(“操作失败错误码: %d 消息: %s”, ret, “Permission denied”); } return 0; }这个例子展示了vfprintf()的使用它接受一个va_list参数使得我们可以将可变参数传递给另一个函数这是实现自定义格式化函数的关键。4. 高级话题、常见陷阱与性能考量4.1 长度修饰符的坑与scanf()的区别这是初学者最容易混淆的地方之一。printf()和scanf()的长度修饰符并不总是对称的。printf()中%f用于double%lf也用于double(C99起)。%Lf用于long double。scanf()中%f用于float%lf用于double%Lf用于long double。原因在printf()的可变参数中float会被提升为double所以只需要%f。而scanf()需要知道确切的地址宽度来写入数据所以必须区分float*和double*。double d; float f; long double ld; scanf(“%f”, f); // 正确传入 float* 地址 scanf(“%lf”, d); // 正确传入 double* 地址 scanf(“%Lf”, ld); // 正确传入 long double* 地址 printf(“%f”, f); // 正确f 被提升为 double printf(“%f”, d); // 正确 printf(“%lf”, d); // 正确C99后允许 printf(“%Lf”, ld); // 正确4.2 缓冲区与输出效率printf()默认是行缓冲的这意味着输出会先放在缓冲区直到遇到换行符\n或缓冲区满才一次性写入标准输出。这会影响调试和性能。4.2.1 调试时立即输出有时程序崩溃前没有输出可能是因为缓冲区未刷新。printf(“开始执行危险操作…“); // 没有 \n可能还在缓冲区 // … 此处程序崩溃 fflush(stdout); // 强制刷新缓冲区确保输出立即显示 printf(“开始执行危险操作…\n”); // 使用 \n 通常会自动刷新行缓冲4.2.2 性能考量在循环中频繁调用printf()进行格式化输出是一个常见的性能瓶颈因为格式化操作和系统调用如write开销较大。// 低效做法 for (int i 0; i 10000; i) { printf(“当前值: %d\n”, i); // 每次循环都进行格式化和I/O } // 高效做法如果条件允许 char buffer[4096]; int offset 0; for (int i 0; i 10000; i) { offset snprintf(buffer offset, sizeof(buffer) - offset, “当前值: %d\n”, i); if (/* 缓冲区快满了 */) { fwrite(buffer, 1, offset, stdout); // 批量写入 offset 0; } } if (offset 0) { fwrite(buffer, 1, offset, stdout); }snprintf()将格式化结果写入字符串缓冲区积累到一定量后再一次性用fwrite写入可以显著减少系统调用次数。4.3 自定义格式化输出有时标准格式无法满足需求比如输出一个自定义结构体。typedef struct { int id; char name[20]; double score; } Student; void print_student(const Student *s) { // 使用固定的格式和宽度让输出对齐美观 printf(“| %-4d | %-15s | %6.2f |\n”, s-id, s-name, s-score); } int main() { Student stu {101, “Alice”, 95.5}; printf(“| ID | Name | Score |\n”); printf(“|------|-----------------|--------|\n”); print_student(stu); return 0; }4.4 常见问题排查速查表问题现象可能原因解决方案输出乱码或异常值1. 格式说明符与参数类型不匹配。2. 参数数量少于格式字符串所需。1. 仔细检查每个%对应的参数类型使用正确的长度修饰符。2. 确保可变参数的数量和顺序与格式字符串一致。浮点数输出精度不对未指定精度使用了默认的6位小数。使用.precision指定所需的小数位数如%.2f。输出没有对齐未使用宽度和标志进行控制。使用%width指定最小宽度用-标志控制左对齐。输出(nil)或奇怪地址用%s打印了未初始化或为NULL的指针。检查指针是否有效指向以\0结尾的字符串。打印前可判断if (ptr ! NULL)。程序崩溃段错误1. 用%s打印了非字符串地址如整数。2. 格式字符串由用户输入且未校验。1. 确保传给%s的参数是char*类型。2.绝对不要将用户输入直接作为printf的第一个参数。使用printf(“%s”, user_input);。输出内容迟迟不显示输出被缓冲且未刷新。在需要立即显示的地方添加\n或调用fflush(stdout);。掌握printf()的细节不仅能让你写出更健壮、更易调试的代码更能帮助你深入理解C语言函数调用、参数传递和内存布局的底层机制。它不是一个简单的输出函数而是一把打开格式化I/O大门的钥匙。下次当你再敲下printf时不妨想想它背后那套精巧的规则或许能帮你省下几个小时的调试时间。

相关新闻

2026企业FTP替代工具横向测评,覆盖信创、跨境、内网场景

2026企业FTP替代工具横向测评,覆盖信创、跨境、内网场景

传统FTP协议诞生于网络发展早期,明文传输、断点续传能力薄弱、跨网传输效率低下的缺陷,已经难以适配当下企业大文件跨境传输、数据合规留存、国产化部署等需求。不少政企、传媒、科研机构陆续着手替换老旧FTP服务,用户普遍关心市面上可用的FT…

2026/10/19 19:18:56 阅读更多 →
IntelliJ IDEA 2026年8月新版本 2026.2 更新内容  免费使用指南

IntelliJ IDEA 2026年8月新版本 2026.2 更新内容 免费使用指南

更新时间:2026-08 支持最新版本:2026.2(2026年7月发布) 支持在线更新 支持Windows、MAC、Linux 新版本更新亮点: 2026.2 主要更新 原生 AI 与 Copilot:内置直连 GitHub Copilot,引入 AI 技…

2026/10/21 1:37:56 阅读更多 →
制造业桌面云有哪些?2026主流产品深度测评与选型指南

制造业桌面云有哪些?2026主流产品深度测评与选型指南

制造业数字化转型进程里,桌面云逐步替代分散式物理工作站,解决图纸泄密、算力浪费、跨厂区运维繁琐等普遍痛点。不少制造企业选型时容易混淆通用办公云桌面与工业专用方案。本文结合2026年桌面虚拟化市场实测数据,列出市面主流适配制造业的桌…

2026/10/18 7:09:44 阅读更多 →

最新新闻

原神小助手:自动战斗、秘境与委托,一份完整使用指南

原神小助手:自动战斗、秘境与委托,一份完整使用指南

原神小助手:自动战斗、秘境与委托,一份完整使用指南 【免费下载链接】genshin_impact_assistant 原神小助手 Genshin Assistant (CN/EN) | 自动战斗,秘境,领日常,半自动委托 项目地址: https://gitcode.com/GitHub_Trending/ge/genshin_impact_assista…

2026/10/21 1:39:25 阅读更多 →
InternVL3-1B-Pretrained vs 完整版:预训练底座到底该不该用?一次讲清选型逻辑

InternVL3-1B-Pretrained vs 完整版:预训练底座到底该不该用?一次讲清选型逻辑

【免费下载链接】InternVL3-1B-Pretrained 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL3-1B-Pretrained 点击查看 免费下载 InternVL3-1B-Pretrained 是 OpenGVLab 推出的 InternVL3 多模态大语言模型(MLLM)的预训练底座&#xff…

2026/10/21 1:39:25 阅读更多 →
一个能在浏览器里跑起来的免费开源三国杀:无名杀本地部署与扩展指南

一个能在浏览器里跑起来的免费开源三国杀:无名杀本地部署与扩展指南

一个能在浏览器里跑起来的免费开源三国杀:无名杀本地部署与扩展指南 【免费下载链接】noname 项目地址: https://gitcode.com/GitHub_Trending/no/noname 无名杀(noname)是一个开源三国杀项目:网页版三国杀,全…

2026/10/21 1:39:25 阅读更多 →
3步部署完整工厂:戴森球计划 FactoryBluePrints 蓝图仓库快速指南

3步部署完整工厂:戴森球计划 FactoryBluePrints 蓝图仓库快速指南

3步部署完整工厂:戴森球计划 FactoryBluePrints 蓝图仓库快速指南 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints 刚开荒《戴森球计划》时,最耗时间…

2026/10/21 1:39:25 阅读更多 →
agentic-awesome-skills v7.2.0 版本解析:Registry 清理、社区技能合入与可解释自动分类

agentic-awesome-skills v7.2.0 版本解析:Registry 清理、社区技能合入与可解释自动分类

AI 技能AI 插件人工智能 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600 agentic skills. Includes CLI, local MCP, c…

2026/10/21 1:39:25 阅读更多 →
Gitnuro 安装与启动指南:在 Linux、Windows、macOS 搭建免费开源的跨平台 Git 图形客户端

Gitnuro 安装与启动指南:在 Linux、Windows、macOS 搭建免费开源的跨平台 Git 图形客户端

Gitnuro 安装与启动指南:在 Linux、Windows、macOS 搭建免费开源的跨平台 Git 图形客户端 【免费下载链接】Gitnuro A FOSS Git multiplatform client for newbies and pros 项目地址: https://gitcode.com/GitHub_Trending/gi/Gitnuro Gitnuro 是一款基于 J…

2026/10/21 1:38:25 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →