什么是抖音粉丝,什么是double型变量

  

  点击上方关注,All in AI中国   

  

  作者――Sergey Vasiliev   

  

  微软公布的项目源是一个很好的分析理由。这次也不例外。今天,我们将看看Infer.NET电码的可疑部分。   

  

     

  

  简要介绍项目和分析程序   

  

  推断。NET是微软专家开发的机器学习系统。该项目的源代码最近已经在GitHub(https://GitHub . com/dot net/infer)上运行。你可以在这里(https://dotnet.github.io/infer/)找到关于该项目的更多详细信息。   

  

  本项目通过PVS-Studio 6.26静态代码分析器进行检查。需要提醒的是,PVS-Studio正在检查Windows、Linux和macOS下C \ C \ C #(和Java)代码中的错误。到目前为止,C #代码只在Windows下分析过。可以下载并尝试在自己的项目中使用(https://www . viva 64 . com/en/PVS-studio-download/)。   

  

  在检查之前,我从GitHub下载了项目源代码,恢复了所需的包(依赖项)并确保项目成功构建。这是必要的,以便分析仪可以访问所有需要的信息来执行全面的分析。点了几下,我通过Visual Studio的PVS-Studio插件运行了解决方案分析。   

  

  对了,这不是微软第一个用PVS-Studio查的项目――还有其他项目:Roslyn(https://www . viva 64 . com/en/b/0363/)、MSBuild(https://www . viva 64 . com/en/b/0424/)、PowerShell(https://www . viva 64 . com/en/b/0447/)、CoreFX(https://www . viva 64 . com/en/b/047/)   

  

  注意了。如果您或您的朋友对Java代码分析感兴趣,可以选择'我要分析Java '给我们写信(https://www . viva 64 . com/en/about-feedback/)。   

  

  接下来,我们来看看代码中的问题。   

  

  它是一个Bug还是一个特性?   

  

  我建议你自己找出错误――这是完全可能的任务。我保证不会有和文章2017 C项目十大错误’(https://www . viva 64 . com/en/b/0565/)一样的内容。因此,请花点时间阅读代码片段后给出的分析器警告。   

  

     

  

     

  

  PVS-Studio警告:V3001“”运算符的左右两侧具有相同的子表达式“double . is infinity(transition 1 . weight . value)”。运行automation . simplification . cs 479(https://www.viva64.com/en/w/v3001/)   

  

  从源代码片段中可以看出,这个方法使用了几个变量——transition 1和transition2。使用相似的名字有时是合理的,但值得记住的是,在这种情况下,不小心在某个地方弄错的可能性会增加。   

  

  所以在无穷远处检查数字时会出现这种情况(double。IsInfinity)。由于误差,相同变量transition1的值。重量。值检查两次。变量跃迁2。第二个子表达式中的Weight.Value必须是选定的值。   

  

  另一个类似的可疑代码。   

  

     

  

  PVS-Studio警告:v 3001“|”运算符的左右两侧有相同的子表达式“BindingFlags”。“公共”。编译器code builder . cs 194(https://www.viva64.com/en/w/v3001/)   

  

  枚举器绑定标志。Public在形成bf变量值时使用两次。此代码包含冗余标志设置操作或使用BindingFlags。公共的,这里必须完成另一个枚举器。   

  

  顺便说一下,这段代码是用一行源代码写的。在我看来,如果以表格的形式格式化,更容易发现问题。   

  

  我们继续。我引用了整个方法体,建议你再找一遍错误。   

>   

  

  

  

找到了? 我们来检查一下!

  

PVS-Studio警告:

  

V3003检测到'if(A){...} else if(A){...}'模式的使用。存在逻辑错误的可能性。检查行:1719,1727。编译器CodeRecognizer.cs 1719(https://www.viva64.com/en/w/v3003/)

  

V3003检测到'if(A){...} else if(A){...}'模式的使用。存在逻辑错误的可能性。检查行:1721,1729。编译器CodeRecognizer.cs 1721(https://www.viva64.com/en/w/v3003/)

  

让我们简化代码,以便问题变得更加明显。

  

  

几个if语句的条件表达式和then分支是重复的。也许,这段代码是用复制粘贴方法编写的,这导致了一个问题。现在事实证明,重复的分支永远不会被执行,因为:

  

如果条件表达式为true,则从相应的对执行第一个if语句的主体;如果条件表达式在第一种情况下为假,则在第二种情况下它也将为假。由于当时的分支包含相同的操作,现在它看起来像冗余代码,这是令人困惑的。也许,这里存在一种不同的问题――必须运行其他检查而不是重复检查。

  

我们继续吧。

  

  

  

PVS-Studio警告:

  

V3004'then'语句相当于'else'语句。运行时RegexpTreeBuilder.cs 1080(https://www.viva64.com/en/w/v3004/)

  

V3004'then'语句相当于'else'语句。运行时RegexpTreeBuilder.cs 1093(https://www.viva64.com/en/w/v3004/)

  

代码看起来非常可疑,因为它包含两个条件语句,其中then和else-branches具有相同主体。可能在这两种情况下,都值得返回不同的值。另一方面,如果它是构思好的行为,那么删除冗余条件语句将是有用的。

  

我遇到了一些更有趣的循环。示例如下:

  

  

PVS-Studio警告:V3020循环内的无条件"中断"。编译器DefaultFactorManager.cs 474(https://www.viva64.com/en/w/v3020/)

  

由于无条件break语句的原因,只执行了一次循环迭代,甚至不使用已更改的控制变量。一般来说,代码看起来很奇怪和可疑。

  

相同的方法(精确复制)发生在另一个类中。相应的分析器警告:V3020循环内的无条件"中断"。Visualizers.Windows FactorManagerView.cs 350(https://www.viva64.com/en/w/v3020/)

  

顺便说一下,我在一个循环中偶然发现了一个无条件的继续语句(分析器通过相同的诊断发现它),但有人说它是一个特殊的临时解决方案:

  

  

让我提醒你,无条件break语句旁边没有这样的注释。

  

让我们继续。

  

  

  

PVS-Studio警告:V3022表达式'resultIndex == null'始终为false。编译器FactorManager.cs 382(https://www.viva64.com/en/w/v3022/)

  

我立即注意到声明和给定检查之间,resultIndex变量的值可能会改变。但是,在检查resultIndex!= null和resultIndex == null之间,值无法更改。因此,表达式resultIndex == null的结果将始终为false,永远不会生成异常。

  

我希望你有兴趣独立搜索bug,即使我没有找到问题的建议,但为了以防万一,我建议你再次这样做。方法代码很小,我将完全引用它。

  

  

  

  

让我们看看这里发生了什么。输入字符串由字符"|"解析。如果数组的长度与预期的长度不匹配,则必须生成异常。等一下...... genres.Length <1 && genres.Length> 3? 由于没有适合表达式(   

这就是分析器阻止我们的原因:V3022 Expression'genres.Length <1 && genres.Length> 3'总是的false。可能应该在这里使用'||'运算符。Evaluator Features.cs 242

  

我遇到了一个可疑的代码操作。

  

  

PVS-Studio警告:V3041表达式从'int'类型隐式转换为'double'类型。考虑使用显式类型转换来避免丢失小数部分。 一个例子:double A =(double)(X)/ Y;。 LDA Utilities.cs 74(https://www.viva64.com/en/w/v3041/)

  

这里有可疑的地方:执行整数除法(variablesaverageDocLength和numUniqueTopicsPerDoc属于int类型),但结果写在double类型的变量中。这引出了一个问题:它是故意制造出来的,还是真正意义上的实数划分?如果变量expectedRepeatOfTopicInDoc属于int类型,这将禁止可能的问题。

  

在其他地方,使用方法Poisson.Sample,其参数是可疑变量expectedRepeatOfTopicInDoc,例如,如下所述。

  

  

averageWordsPerTopic属于int类型,在其使用位置被强制转换为double。

  

这是另一个使用地点:

  

  

请注意,变量具有与原始示例中相同的名称,仅用于expectRepeatOfWordInTopic初始化实数的分割(由于显式numDocs强制转换为double类型)。

  

总的来说,分析器突出显示的上面提到的起始源代码片段值得关注。

  

  

PVS-Studio警告:V3041表达式从'int'类型隐式转换为'double'类型。考虑使用显式类型转换来避免丢失小数部分。 一个例子:double A =(double)(X)/ Y;。运行时ProductExp.cs 137(https://www.viva64.com/en/w/v3041/)

  

分析器再次发现了一个整数除法的可疑操作,因为2和3是整数数字文字,表达式2/3的结果将为0。结果,表达式如下所示:

  

  

你必须承认,这有点奇怪。有几次我又回到这个警告,试图找到一个技巧,而不是试图将其添加到文章中。这个方法充满了数学和公式(坦白地说,拆解这些方法并不具有吸引力),这里有很多值得期待的东西。此外,我尽可能地对警告持怀疑态度,我在文章中加入了警告,并描述了我对它们的初步深入研究。

  

然后我明白了――为什么你需要这样一个乘数0,写成2/3?因此,无论如何,这个地方值得一看。

  

  

PVS-Studio警告:V3080可能无效撤销引用。考虑检查"价值"。 编译器WriteHelpers.cs 78(https://www.viva64.com/en/w/v3080/)

  

基于条件的相当公平的分析器警告。表达式value.Equals(defaultValue)中可能出现无效撤销引用,ifvalue == null。 由于此表达式是运算符||的右操作数,因此对于它的求值,左操作数必须具有false值,并且为此目的,至少有一个变量defaultValue \ value不等于null。最后,如果defaultValue!= null,并且value == null:

  

defaultValue== null - > false;defaultValue== null && value == null - > false; (没有执行价值检查)value.Equals(defaultValue) - > NullReferenceException,作为值 - null。让我们看看另一个示例:

  

  

PVS-Studio警告:V3080可能的无效撤销引用。考虑检查'traitFeatureWeightDistribution'。推荐FeatureParameterDistribution.cs 65(https://www.viva64.com/en/w/v3080/)

  

让我们省略额外的字符串,只留下评估布尔值的逻辑,以便更容易理清:

  

  

同样,运算符||的右操作数仅在评估左边的结果为false时才进行评估。左操作数可以取false值,包括traitFeatureWeightDistribution == null和ofbiasFeatureWeightDistribution!= null。然后是运算符||的右操作数将被评估,并调用traitFeatureWeightDistribution.All,所有这些操作将导致抛出ArgumentNullException。

  

另一段有趣的代码:

  

  

PVS-Studio警告:V3095在对null进行验证之前使用过'quantiles'对象。检查线:91,92。运行时OuterQuantiles.cs 91(https://www.viva64.com/en/w/v3095/)

  

请注意,访问quantiles.Length属性,然后检查分位数是否为null。最后,如果quantiles == null,该方法将抛出异常,但是错误的异常会被抛出到错误的位置。可能是线条倒置了。

  

如果你已成功应对前面列出的错误,我建议你喝一杯咖啡并尝试在下面的方法中发现错误。为了使它更有趣,我将完全引用方法代码。

  

(full-size:https://www.viva64.com/media/images/content/b/0590_InferNET/image3.png)

  

  

好吧,好吧,这是一个玩笑。让我们简化任务:

  

  

分析仪对此代码发出以下警告:V3008'lowerBound'变量被连续赋值两次。也许这是一个错误。检查行:324,323。运行时GaussianOp.cs 324(https://www.viva64.com/en/w/v3008/)

  

实际上,在最后一个else分支中,下界(lowerBound)变量的值被连续分配两次。显然(从上面的代码判断),上界(theupperBound)变量应该参与其中一个赋值。

  

让我们继续。

  

  

PVS-Studio警告:V3081'r'计数器未在嵌套循环中使用。考虑检查"c"计数器的使用情况。CommandLine ClassifierEvaluationModule.cs 459(https://www.viva64.com/en/w/v3081/)

  

请注意,内循环计数器-r未在此循环的主体中使用。因此,事实证明,在内部循环的所有迭代中,执行具有相同元素的相同操作――在索引中,还使用外部循环的计数器,而不是内部循环(r)中的一个。

  

让我们看看其他有趣的问题。

  

  

PVS-Studio警告:V3117未使用构造函数参数"useLazyQuantifier"。 运行时RegexpFormattingSettings.cs 38(https://www.viva64.com/en/w/v3117/)

  

在构造函数中没有使用参数useLazyQuantifier。它看起来特别可疑,因为在类中,属性是用适当的名称和类型定义的――UseLazyQuantifier。显然,人们忘了通过相应的参数进行初始化。

  

我还遇到了几个有潜在危险的事件处理程序。其中一个示例如下:

  

  

PVS-Studio警告:V3083事件'Started'的不安全调用,NullReferenceException是可能的。在调用之前,请考虑将事件分配给局部变量。Evaluator RecommenderRun.cs 115(https://www.viva64.com/en/w/v3083/)

  

事实上,在检查null不等式和处理程序调用之间,如果在测试null和调用事件处理程序之间,事件没有订阅者,将抛出NullReferenceException异常,则会发生事件取消订阅。例如,要避免此类问题,可以将对委托链的引用保留到局部变量中,或使用"?"运算符来调用处理程序。

  

除上述代码片段外,还发现了其他35个这样的地方。

  

顺便说一句,发生了785 个V3024警告。在使用运算符'!='或'=='比较实数时会发出V3024警告。我不会详述为什么这种比较并不总是正确的。有关这方面的更多信息,请参阅文档,还有一个指向StackOverflow的链接。(https://stackoverflow.com/questions/1398753/comparing-double-values-in-c-sharp)

  

考虑到经常满足公式和计算的事实,这些警告即使被置于第3级也很重要(因为它们几乎与所有项目无关)。

  

如果你确定这些警告"无关紧要",只需单击一下即可将其删除(https://www.viva64.com/en/m/0013/),从而减少分析器触发的总次数。

  

  

结论

  

我希望你从这篇文章中学到了一些新的东西,或者至少有兴趣阅读它。

  

我希望开发人员能够快速解决问题所在的地方,我想提醒一下,犯错是可以的,因为我们是人。但这也是为什么我们需要额外的工具,如静态分析工具来找到一个人忘记/做错的东西,对吧?无论如何,祝你的项目好运!

  

此外,请记住,静态分析器的最大用量是在正常使用时获得的。

  

相关文章