性能比安全更重要Rust和C++是两种流行的系统级开发语言。多年来,业界对C++的关注主要集中在性能上,我们也不断听到客户和安全研究人员的反馈:他们希望C++在语言层面有更多的安全编码准则。在安全编程方面,C++通常被认为落后于Rust。借鉴Rust在安全编码方面的特点,我们在VisualStudio2019v16.7的C++CoreCheck中添加了四个编码安全指南。让我们来看看。switch语句没有默认标签。Rust中的模式匹配结构类似于C++中的switch语言结构。它们的主要区别在于Rust要求开发人员覆盖所有模式匹配的可能性,或者通过为每个模式编写一个显式处理程序,或者在没有其他模式匹配时添加默认处理程序。.例如,以下Rust代码将无法编译,因为它缺少默认处理程序。这是一个巧妙的安全功能,因为它可以防止这种易于制作但不易发现的编程错误。如果在switch语句中使用了枚举类型并且没有计算每个枚举值,VisualStudio将警告开发人员并发出C4061和C4062。但是,对于其他类型,例如整数,则没有这样的警告。在这个版本中,我们引入了安全编码准则:对于非枚举类型(如char、int),如果switch语句中没有默认处理标签,VisualStudio会发出警告。可以在项目的规则设置中选择三种不同的规则,然后进行代码分析。>C++CoreCheckStyleRules>C++CoreCheckRules>MicrosoftAllRules让我们用C++重写上面的Rust例子。如果我们删除默认标签,VisualStudio将给出以下警告:switch语句中未注释的fallthroughRust中模式匹配的另一个限制是它们不支持case语句中的隐式跳转。跳。在C++中,下面的代码可以完美的通过编译器的检查。上面的C++代码看起来很合理,但是case语句中的隐式跳转很容易成为程序的bug。例如,如果开发人员忘记在each(food)调用之后添加break语句,代码仍然可以编译,但结果会大不相同。如果项目的规模非常大,这样的bug就很难追踪到。好在C++17增加了注解[[fallthrough]],主要目的是在不同的case语句中进行隐式跳转。这样,在上面的例子中,开发者就可以通过这个注解来向编译器表明他确实想要这个行为。在VisualStudio2019v16.7中,如果代码中存在不使用[[fallthrough]]注解的隐式跳转,编译器会给出C26819警告。当VisualStudio执行代码分析时,默认情况下启用此规则。为了解决上面的警告,可以在case语句中加上[[fallthrough]]注解,如下图:昂贵的复制操作Rust和C++的主要区别之一是Rust使用move(移动)语义默认代替复制(copy)。例如:这意味着当你真正需要复制语义时,你需要使用显式复制语句,如下图所示:C++不同,它默认是复制语义。通常,这没什么大不了的,但有时它会导致一些错误。一个经常出现的例子是使用range-for语句。让我们看一下这个例子:在上面的代码中,向量中的每个基元在循环的每次迭代中都被复制到p。如果元素是一个大结构,复制操作会很昂贵,这种情??况不容易看到。为了避免这种不必要的拷贝,我们在C++CoreCheck中加入了编码指南,建议开发者去掉这种拷贝操作,如下图所示:下面是判断是否需要进行某种拷贝操作的方法:如果类型的大小大于平台相关指针大小的两倍,并且类型不是智能指针或gsl::span、gsl::string_span或std::string_view之一,则认为不需要复制。这意味着对于较小的数据类型,例如整数,不会触发警告。对于较大的类型,例如上面示例中的Person类型,此复制操作被认为是昂贵的(不必要的)并且编译器将发出警告。这条规则的最后一点是,如果循环体中的变量发生变异,则不会触发警告,如下图所示:如果使用的容器不是const类型,可以通过将对象修改为a来避免不必要的参考类型副本但是,这样的修改会引起新的副作用。因此,此警告仅建议将循环变量标记为const引用,如果循环变量不能合法地标记为const类型,则不会触发此警告。默认情况下启用此编码准则。auto类型变量的复制最后一个检查规则涉及auto类型变量的复制操作。考虑以下Rust代码,其中对分配了引用的变量进行类型解析。由于Rust要求,在大多数情况下复制必须是显式的,因此在上面的示例中,密码类型在分配后自动解析为不可变引用,并且没有执行昂贵的复制操作。另一方面,考虑以下C++代码:在上面的代码中,密码类型被解析为std::string,即使getPassword的返回类型是对字符串的const引用。结果,PasswordManager::password的内容被复制到局部变量password中。这是与返回指针的函数的比较:分配引用和指向标记为auto的变量的指针之间的行为差??异并不明显,可能导致不必要和意外的复制。为了防止由于这种行为而导致的错误,检查器检查所有初始化实例,这些实例来自对标记为auto的变量的引用。如果使用与范围检查相同的启发式方法认为生成的复制操作代价高昂,检查器将发出警告,建议将变量标记为const引用类型。与范围检查一样,只要变量不能合法地标记为const,就不会发出此警告。另一种不发出警告的情况是每当从临时对象派生引用时。在这种情况下,一旦临时对象被销毁,使用constauto引用将导致对已销毁临时对象的“悬挂”引用。默认情况下启用此编码指南。总结可以看(写)到这里,我觉得应该是男的。一些编码准则(例如,当声明变量时必须初始化),最好成为你的肌肉记忆。在编写某种代码结构时,完成安全编码原则的是你的肌肉,而不是你的大脑。最后,MicrosoftVisualC++团队的博客是我最喜欢的博客之一。里面包含了很多关于VisualC++的知识和最新的开发进展。大浪淘沙,如果你对VisualC++这个古老的技术还那么感兴趣,可以常去看看他们(或者我)。
