写 C/C++ 时,有一类错误特别典型:
if (i = 3)
{
// ...
}
本来想写的是:
if (i == 3)
{
// ...
}
但手一快,就把 == 写成了 =。麻烦的是,这种代码在 C/C++ 里往往不是语法错误,它通常还能编译,因为赋值表达式本身有值,放进条件里是合法的。MSVC 对这种情况对应的就是 C4706:assignment used as a condition。而且这条警告是 4 级警告,需要开到 /W4 才能稳定看到;Visual Studio IDE 的默认警告级别通常是 /W3。
所以我对这个问题的态度一直都不是二选一,而是分阶段处理:
前期我更推荐直接写成“常量在前”,先把低级手滑错误按住;
后期再逐步过渡到更自然的写法,同时把 Visual Studio 的警告、错误提升和代码分析全开起来。这样既实用,也更符合长期工程化开发。
一、我为什么前期推荐“常量在前”
我前期更愿意写:
if (3 == i)
{
}
原因很简单。这样一来,哪怕手滑写成:
if (3 = i)
{
}
编译器也会直接报错,因为常量不能作为赋值左值。
这不是最优雅的长期风格,但对刚开始写条件判断的人来说,它确实有防呆效果。相比“读起来是不是最自然”,我更在意前期先别把最常见、最隐蔽的错误写进去。毕竟 if (i = 3) 这种问题最烦的地方就在于:它常常不是编不过,而是编得过、跑得动、结果还错。MSVC 官方对 C4706 的说明也很明确:如果本意是比较关系,就应该写 ==,不是 =。
二、我不把“常量在前”当成最终风格
虽然我前期推荐这么写,但我不觉得这应该成为永久规范。
因为代码写久了以后,绝大多数人读起来更自然的还是:
if (i == 3)
{
}
这更符合正常的阅读顺序:判断变量 i 是否等于 3。反过来写成 3 == i,虽然能防手滑,但长期看可读性一般,也不算现代 C++ 工程里最理想的表达方式。真正成熟的做法,应该是正常写代码,然后把工具链配置好,让 IDE 和编译器帮我拦这类错误。微软也明确建议尽量在最高的常规警告级别 /W4 下编译。
三、我的过渡路线:从写法防错,慢慢过渡到工具防错
我的思路一直很明确:
前期先靠写法防错,先写:
if (3 == i)
{
}
等对 = 和 == 足够敏感之后,再逐步回到自然写法:
if (i == 3)
{
}
与此同时,把 Visual Studio 的警告、错误处理和代码分析一起配好。这样后面靠的就不再是“绕一下写法”,而是靠一套更稳定的开发环境来兜底。
四、我在 Visual Studio 里怎么配
1. 先把警告等级开到 /W4
我一般会到:
项目 -> 属性 -> 配置属性 -> C/C++ -> 常规 -> Warning Level
把它设成:
Level4 (/W4)
因为 C4706 本身就是 level 4 warning。不开到 /W4,这类问题很可能根本不提醒。微软文档对 /W0 到 /W4 的说明里也写得很清楚:IDE 默认通常是 /W3,而 /W4 会打开更多高价值警告。
2. 再把警告视为错误
接着我会把:
项目 -> 属性 -> 配置属性 -> C/C++ -> 常规 -> Treat Warnings as Errors
设为开启,也就是 /WX。
这样做的意义非常直接:
只要这类问题出现,就别让我继续往下编译。微软文档也说明了 /WX 会把警告按错误处理;一旦这么做,带有警告的编译会直接失败。
3. 需要的话,再单独把 4706 升格
如果项目里历史包袱比较多,不想一下子把所有警告都变成错误,我也会只针对这一类问题下手,直接用 /we4706,也就是只把 4706 当成错误处理。MSVC 的 warning-level 选项支持这种按编号提升的方式。
4. 再把代码分析打开
我还会打开:
项目 -> 属性 -> 配置属性 -> Code Analysis
微软文档里给出的路径就是在项目属性页里进入 Code Analysis 页面,可以开启 Microsoft Code Analysis,也可以配置为构建时自动运行。
这一步的价值在于,它不只是从“编译器警告”的角度看问题,还会从“代码缺陷”的角度再扫一遍。像把常量赋给变量再放进布尔测试上下文这种情况,代码分析对应的就是 C6282。微软对这条规则的描述也很直接:在测试上下文中把常量赋给变量,几乎总是错的,应该把 = 改成 ==。
五、我现在更认同的写法和做法
所以我现在的结论很简单:
前期我推荐先用“常量在前”:
if (3 == i)
{
}
这不是因为它最优雅,而是因为它对压制低级手滑真的有用。
后期我会慢慢过渡到自然写法:
if (i == 3)
{
}
然后把主要防线交给这些东西:
/W4/WX- 必要时
/we4706 - Microsoft Code Analysis
- 构建时自动运行代码分析
这样做的好处是,代码风格可以逐步回归自然,开发习惯也会越来越工程化。
六、顺手补一句:真的想在条件里赋值怎么办
有时候条件里赋值不是手滑,而是故意的,比如一些传统写法会这么写:
while ((ch = getchar()) != EOF)
{
}
MSVC 官方对 C4706 的说明是:如果赋值确实是有意为之,就应该把它写得足够明确,比如放进括号里,或者显式再去比较非零、非空。Visual Studio 2022 近年的改进里也提到,编译器现在会识别“我故意用括号包住赋值”的这种写法,并据此抑制 C4706。
结语
我对这个问题的看法一直都不极端。
“常量在前”不是我心里最理想的长期风格,但它对前期防手滑确实有效;
而 IDE 警告和代码分析,才是我更认可的长期方案。
所以我更认同的路线不是非此即彼,而是分阶段:
先用“常量在前”减少低级错误,
再慢慢过渡到 i == 常量 的自然写法,
最后把防错这件事真正交给 Visual Studio 的警告系统和代码分析。这样走下来,代码风格、开发习惯和工程意识,都会更顺。
Comments NOTHING