VS中_CRT_SECURE_NO_WARNINGS宏的作用、用法与避坑指南

1. 为什么你的VS里总是报“CRT_SECURE_NO_WARNINGS”这个错

用微软系编译器(Visual Studio、VS Code搭配MSVC工具链)写C/C++程序的人,几乎都见过这行报错:

error C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead.

第一次遇到的人往往一脸懵:scanf不是标准库函数吗?怎么就不安全了?我又没写什么危险代码,编译器凭什么拦我?

这个提示的本质是微软对标准C库函数的“安全策略”:在Windows平台上,像scanf、strcpy、strcat、fopen这批老牌函数,存在缓冲区溢出或被恶意利用的风险,微软默认把它们标记为“弃用”,编译时直接抛C4996警告,甚至直接中断编译。而对应的“安全版本”scanf_s、strcpy_s等是微软自己定义的扩展,并不属于C标准。所以你在Linux上用GCC编译同一段代码,完全不会碰到这个报错。

解决这个问题最常见、最省事的方法,就是在源文件顶部加一行宏定义:

#define _CRT_SECURE_NO_WARNINGS 1

加完之后,世界清净了。今天这篇就专门聊聊这个方法:为什么加这一行就有效、加在什么位置、有没有副作用、对应生产环境下的更优解法是什么。

2. 这个宏到底做了什么,为什么能压制C4996

2.1 预处理指令的生效时机

要理解这个宏,先得弄明白编译的四个阶段:预处理、编译、汇编、链接。#define属于预处理指令,在真正的编译开始之前就处理完毕。_CRT_SECURE_NO_WARNINGS是一个“预定义标识符”,当编译器在编译头文件时,看到这个标识符存在,就知道“用户主动放弃了安全警告”,于是不再令C4996生效。

可以这样理解:微软在它的标准库头文件里写了大段类似这样的逻辑:

#ifdef _CRT_SECURE_NO_WARNINGS #define scanf scanf #else #pragma message("This function may be unsafe") #endif

实际源码更复杂,但思路一致。这个宏不是“关闭某一行代码”,而是改变了MSVC对“弃用函数”的检查开关。

2.2 为什么后面要写“1”

很多人只写#define _CRT_SECURE_NO_WARNINGS,不写后面的1,其实也有效。这个1不是必须的,但写上有好处:当宏被当作条件表达式使用时,1代表“真”,语义更明确。微软本身也没规定值必须是1,只要宏被定义了就行。不过项目里既有#define又有#undef的时候,显式的1能避免一些工具链的静态分析误判。

2.3 位置差一点,效果差很多

这个宏必须放在所有头文件包含之前,否则无效。为什么?因为头文件里的条件判断发生在头文件被解析时,如果你的代码是这样:

#include <stdio.h> #define _CRT_SECURE_NO_WARNINGS 1

那么stdio.h在被包含的瞬间,_CRT_SECURE_NO_WARNINGS还未定义,等头文件解析完,宏才被定义,但那时候C4996的开关已经按“未定义”处理完了。所以正确的写法必须是:

#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h>

当然,如果你用的是预编译头(stdafx.h或pch.h),把宏放在预编译头的最顶部更省事,因为每个源文件都会先包含预编译头。

3. 设置这个宏的五种方法,逐个说清楚

3.1 方法一:源文件顶部直接写

这是最原始、最直观的方式。在每一个需要用到不安全函数的.c或.cpp文件的第一行写上:

#define _CRT_SECURE_NO_WARNINGS 1

很短,但问题也明显:如果工程有几十个源文件,每个文件都要加一遍,漏掉一个就继续报错。而且以后想移除这个宏,也得挨个找、挨个删。

这个方法适合:单个小程序、算法练习、刷题测试,一个文件搞定一切的时候。

3.2 方法二:项目属性里全局设置

在Visual Studio中,可以这样操作:

  1. 右键项目名,选择“属性”。
  2. 左侧选择“C/C++” -> “预处理器”。
  3. 右侧“预处理器定义”一栏,点下拉箭头,选择“编辑”。
  4. 添加_CRT_SECURE_NO_WARNINGS,注意这里不用写#define,也不用写1,直接写宏名。
  5. 整行内容用分号分隔,比如_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG。
  6. 确定保存后,整个项目的所有源文件都生效。

这个方法的核心好处是“一处配置,全局生效”,不用改任何代码。缺点是把“代码层面的选择”藏到了工程配置里,换一台电脑、换一个工具链,配置跟着工程文件走还好,如果代码丢到Linux上用Makefile编译,这个设置就丢失了。

3.3 方法三:新建一个头文件统一包含

如果你仍想从代码层面管理,但不想每个源文件都写一行,可以自建一个头文件,比如叫common.h,内容如下:

#ifndef COMMON_H #define COMMON_H #define _CRT_SECURE_NO_WARNINGS 1 #endif

然后每个源文件第一行包含它:

#include "common.h"

这比在每个文件里写#define多一些隔离性。将来想改宏开关,只需要改common.h一处。如果项目中原本就有统一的基础头文件,直接把宏加进去即可。

3.4 方法四:编译器命令行参数

如果你在命令行下编译,例如:

cl /D _CRT_SECURE_NO_WARNINGS main.c

或者用CMake,在CMakeLists.txt中:

add_compile_options(/D _CRT_SECURE_NO_WARNINGS)

这种方法适合自动化构建,不污染源码。在CI/CD流水线里,用命令行参数控制要比改代码安全得多,因为代码是共享的,而构建参数是按环境区分的。

3.5 方法五:使用#pragma warning替代宏

宏控制的是_CRT_SECURE_NO_WARNINGS这个标识符,但如果你只想临时压掉某个特定的警告,可以用#pragma warning:

#pragma warning(disable:4996)

或者只针对某个函数:

#pragma warning(push) #pragma warning(disable:4996) scanf("%d", &a); #pragma warning(pop)

这种方法的优点是范围可控,避免“全局关闭警告”带来的隐患。缺点是代码变啰嗦,而且每次处理一个警告都要写三行。对于项目里已经明确抛弃不安全函数、只偶尔兼容旧代码的情况,这个方法反而更合适。

4. 手把手实操:从新建项目到编译通过

完整走一遍流程,以Visual Studio 2022为例,写一个最经典的示例——用scanf读取用户输入并打印。

4.1 新建一个空控制台项目

打开VS,选择“创建新项目” -> “控制台应用” -> 项目名称任意,比如DemoConsole。创建完成后,会自动生成一个包含main函数的源文件。

4.2 尝试编译,看到C4996

把main.cpp内容改成:

#include <stdio.h> int main() { int number = 0; printf("Enter a number: "); scanf("%d", &number); printf("You entered: %d\n", number); return 0; }

按下Ctrl+F5运行,或者直接Ctrl+B编译。输出窗口会出现:

error C4996: 'scanf': This function or variable may be unsafe...

有些VS版本的配置可能只是警告不会中断编译,但很多课程作业、刷题环境会把“警告视为错误”(/WX),于是直接无法生成。

4.3 添加宏定义,验证效果

在#include <stdio.h>上面加一行:

#define _CRT_SECURE_NO_WARNINGS 1

再重新编译,C4996消失,程序正常输出。这就是最核心的“方法”本身。

4.4 用属性面板设置,避免改代码

如果你不想在代码里加宏,回到C4996报错的状态,然后按3.2节的方法,在项目属性的“预处理器定义”中加上_CRT_SECURE_NO_WARNINGS,重新编译,效果一样,代码保持干净。

4.5 命令行编译时的完整示例

如果你装了VS的开发者命令行工具,可以这样验证:

cl /WX /D _CRT_SECURE_NO_WARNINGS main.c

其中/WX表示把警告当错误。如果去掉/D _CRT_SECURE_NO_WARNINGS,编译必定失败;加上后,编译通过。这个对比测试能让你直观感受到宏的威力。

5. 这个宏不是银弹,误用会有什么坑

5.1 掩盖问题而不是解决问题

_CRT_SECURE_NO_WARNINGS只是让编译器闭嘴,并没有让代码变安全。scanf("%s", buf)该溢出还是会溢出,strcpy该越界还是会越界。把警告压下去,就等于把风险压到了运行时,调试难度陡增。

举个例子,早期某项目里大量使用了strcpy,大家为了省事全都加了_CRT_SECURE_NO_WARNINGS。直到某天线上环境发现偶发性的内存内容被篡改,排查了好久,最后定位到一句strcpy(dest, src),其中dest分配了16字节,实际拷入了32字节,直接把相邻的内存区域覆盖了。这类问题在加宏后依然存在,只是平时不被提醒。

所以正确的心态是:这个宏是开发期的免打扰工具,不是生产环境的安全背书。

5.2 项目迁移到Linux/跨平台时的编译差异

_CRT_SECURE_NO_WARNINGS是微软平台特有的,GCC和Clang上根本没这个宏,也不认识它。它们的警告体系不同,不靠这个宏控制。所以把代码迁到Linux后,源文件里多出来的这一行会被当作普通宏定义,不影响编译,但也不起任何作用。GCC会用-Wformat-truncation、-Wstringop-overflow等更细粒度的选项来提示危险用法。

跨平台项目里更合适的做法是封装一层“安全输入/复制”函数,或者在构建系统里区分平台参数,而不是依赖这一个宏。

5.3 和预编译头配合时的顺序问题

如果你用stdafx.h(老项目)或pch.h(新项目),宏必须放在这个预编译头的最顶部。很多新手把宏写在某个.cpp文件里,而.cpp文件的第一行是#include "pch.h",预编译头先被解析,宏定义在预编译头之后才出现,结果还是不生效,甚至编译器会提示“必须放在预编译头之前”。

最稳妥的做法是:打开pch.h,第一行就是#define _CRT_SECURE_NO_WARNINGS 1,然后再包含其他标准库头。

5.4 静态分析工具会“以你为敌”

像VS自带的代码分析器、Clang-Tidy这类工具,即使编译通过,也会在静态分析结果里报告“使用了不安全函数”。宏只影响编译器,不等于影响分析器。如果团队把静态分析结果作为质量门禁,这个宏拦不住门禁失败。

6. 什么时候不该用这个宏,替代方案盘点

6.1 使用微软的“安全函数”版本

既然微软提供了scanf_s、strcpy_s、fopen_s,其实更符合其设计意图的是改用这些函数。例如:

char buf[20]; scanf_s("%s", buf, (unsigned)sizeof(buf));

scanf_s要求显式传递缓冲区大小,编译器和运行时都会做校验。缺点是这些函数不是C标准,跨界到Linux时又得写一层封装。如果项目长期锁定Windows平台,迁移成本可接受,那用安全版本远比压警告靠谱。

6.2 用标准C11的边界检查接口

C11标准里其实引入了scanf_s等带_s后缀的“边界检查”函数,只是一直标为“可选”。微软的实现符合C11 Annex K的精神,但和其他编译器不完全兼容。所以如果追求标准合规,这条路还得等生态完善。

6.3 用fgets替代scanf

对于“读取输入”的场景,最推荐的跨平台方案是用fgets或getline。比如:

#include <stdio.h> int main() { char line[64]; int number = 0; if (fgets(line, sizeof(line), stdin) != NULL) { number = atoi(line); } printf("You entered: %d\n", number); return 0; }

这样既不用scanf,也不涉及缓冲溢出,GCC和MSVC都不报警告。

6.4 用C++的流操作

如果项目是C++,最干净的做法是:

#include <iostream> int main() { int number; std::cin >> number; std::cout << "You entered: " << number << std::endl; return 0; }

C++的iostream没有同样性质的C4996警告。这也是为什么很多教学代码用C但实际是C++编译器编译时,才会碰到这个报错——纯C编译器(如GCC)不报C4996,只有MSVC这么干。

7. 我自己在实际项目里的做法

用了几年之后,我对这个宏的态度分成了两种情况。

如果是在刷题、做Demo、写临时脚本,我直接第一行加上宏,快速通过编译,把时间花在逻辑上。这种场景下,代码的生命周期以小时计,不会进生产环境,安全警告优先级让我觉得没必要过度纠结。

如果是在正式项目,我不会在源码里加这个宏,而是走项目属性或CMake参数。因为正式项目会持续迭代,我希望每次编译时,编译器都能提醒我还有多少“旧代码”没迁移到安全模式。一旦加了宏,这个提醒就消失了。我会把宏当作过渡措施:先把项目的警告数量归零,然后逐步在代码评审中把scanf换成fgets,把strcpy换成memcpy_s或strncpy。迁移完一个模块,就删掉一个源文件里的#define,最后彻底移除。

7.1 共享代码库的小技巧

如果你的代码是团队成员共享的,一定要在README或代码注释里写明为什么加这个宏。因为新手看到_CRT_SECURE_NO_WARNINGS很容易误解为“项目有安全问题被压下去了”,进而产生恐慌。注明“仅用于Windows本地教学环境,生产环境请勿照搬”能避免不少沟通成本。

7.2 配合#pragma warning微调

单文件里如果只有一种不安全函数,我建议用#pragma warning局部禁用,比如:

#pragma warning(push) #pragma warning(disable:4996) #include <stdio.h> #pragma warning(pop)

这里的写法是从源头压制针对头文件的警告,但依然不建议长期使用。通常做法是:宏定义解决“全项目”的压制,pragma解决“单点”的压制,两者各司其职。

7.3 一个常常被忽略的细节

_CRT_SECURE_NO_WARNINGS对C++文件同样有效,不是只能用在.c文件上。另外,它不只是管scanf,还管strcpy、strcat、sprintf、fopen、gets这些旧式函数。所以当你一次看到“n个C4996”时,这一个宏能全部压掉。

8. 快速排查清单:加宏后还是报错怎么办

有时候明明加了宏,C4996还是冒出来,常见原因有这么几种:

症状可能原因解决办法
宏写在#include之后预处理顺序不对把#define移到所有#include之前
宏写在pch.h但源文件先包含其他头预编译头顺序冲突检查项目是否启用了强制预编译头
宏写在.c源文件但warning来自.cpp文件编译对象不匹配确认所有报错文件都加了宏或全局配置
项目属性设置了/WX且宏未生效属性配置没应用到当前配置检查“活动解决方案配置”是Debug还是Release
宏名拼写错误少了下划线,或多了空格复制标准写法_CRT_SECURE_NO_WARNINGS
使用了nginx或第三方库的旧代码源码被定义在系统目录用#pragma warning(disable:4996)包住include

遇到宏设置正确但还报错的情况,优先怀疑预编译头。我曾经帮人排查过一个项目,源文件里明明有宏,但预编译头管理混乱,某些源文件没走预编译头,宏的定义顺序还是不对。

9. 从更长远的视角看待C4996警告

C4996本身不是什么严重的语法错误,它代表的是“平台厂商对旧代码的安全态度”。微软敢把标准库函数标为弃用,是因为它有足够的影响力,也因为它提供的替代方案基本覆盖了日常需求。这对开发者来说,是一把双刃剑:一方面,安全感提升了;另一方面,“弃用”两个字意味着你写的代码不具备最佳跨平台性。

我见过不少入职一两年的开发者在简历上写“精通C/C++”,但连这段警告的含义都解释不清楚。这种基础细节,恰恰是区分“用过”和“理解”的试金石。每次遇到这个警告,多花十秒想想它背后的库函数设计逻辑,比单纯加一行宏然后忘掉要值得多。

我个人现在的习惯是:新建项目时,尽量避开会导致C4996的函数;维护旧项目时,先用宏过渡,再逐步替换。这个顺序既能保证效率,又不至于让代码变成一堆压着警告的参数拼凑。核心原则很简单——编译器帮你发现问题,你有权选择忽略,但你得知道忽略的是什么、代价是什么。

相关新闻

Product Hunt热榜复盘:从票数与评论区看产品冲榜策略

Product Hunt热榜复盘:从票数与评论区看产品冲榜策略

我每天早上都会打开 Product Hunt 的热榜页面&#xff0c;这已经是雷打不动的习惯。2026-03-31 的这份榜单&#xff0c;我点开之前其实没抱太大期待——毕竟“每日热榜”四个字听起来就像流水账。但真正逐条把当天上榜的产品翻完、把评论区看完、把背后几位创始人的发布策略捋清…

2026/10/19 18:05:50 阅读更多 →
C#类成员详解:字段、属性、构造函数及面试要点

C#类成员详解:字段、属性、构造函数及面试要点

1. 为什么"简述类成员"值得你认真准备 我以前参与技术面试时&#xff0c;经常用"简述类的成员"这道题做开场。原因很简单&#xff1a;它听起来基础得不像话&#xff0c;但实际考察的层次非常深。一个候选人如果连这道题都答得有结构、有主次&#xff0c;那…

2026/10/19 18:05:50 阅读更多 →
达梦8 DSC共享存储集群在VMware虚拟化环境中的部署实践与踩坑指南

达梦8 DSC共享存储集群在VMware虚拟化环境中的部署实践与踩坑指南

1. 项目拆解&#xff1a;达梦8 DSC在虚拟化环境中的定位和难点1.1 达梦8 DSC到底是什么达梦8的DSC全称是Data Shared Cluster&#xff0c;也就是共享存储集群方案。它的逻辑跟很多人熟悉的Oracle RAC类似&#xff1a;两个或多个实例节点&#xff0c;通过共享存储访问同一份数据…

2026/10/19 18:05:50 阅读更多 →

最新新闻

Java 基础核心知识学习记录

Java 基础核心知识学习记录

/* 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 18:47:41 阅读更多 →
3分钟理清AQS与其子类的那些事

3分钟理清AQS与其子类的那些事

/* 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 18:41:37 阅读更多 →
【拯救HMI】:工业4.0时代,为什么你的设备还长着一张“2.0时代的脸”?

【拯救HMI】:工业4.0时代,为什么你的设备还长着一张“2.0时代的脸”?

/* 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 18:40:37 阅读更多 →
Java程序员转AI开发:不用先清空重学,重点补这4项能力

Java程序员转AI开发:不用先清空重学,重点补这4项能力

/* 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 18:40:37 阅读更多 →
《软件工程原理与实践》Lab3——卷积神经网络

《软件工程原理与实践》Lab3——卷积神经网络

/* 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 18:39:36 阅读更多 →
m3u8视频下载全攻略:从HLS协议原理到在线工具实操与避坑指南

m3u8视频下载全攻略:从HLS协议原理到在线工具实操与避坑指南

简介&#xff1a;这是一款面向流媒体视频下载需求的 m3u8 在线下载与提取工具&#xff0c;适合需要将 HLS 流媒体保存为本地视频的普通用户、视频运营人员及前端开发者。工具围绕 M3U8 清单解析、TS 分片多线程下载和自动合并设计&#xff0c;同时提供网页端交互界面&#xff0…

2026/10/19 18:38:35 阅读更多 →

日新闻

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

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

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

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

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

周新闻

基于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/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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 阅读更多 →