Nous y voilĂ , continuons d'explorer le code des calculatrices! Aujourd'hui, nous allons jeter un Ćil au projet appelĂ© SpeedCrunch, la deuxiĂšme calculatrice gratuite la plus populaire.
Présentation
SpeedCrunch est une calculatrice scientifique de haute précision dotée d'une interface utilisateur rapide pilotée par clavier. Il s'agit d'un logiciel gratuit et open source, sous licence GPL et fonctionnant sous Windows, Linux et macOS.
Le code source est disponible sur
BitBucket . J'ai Ă©tĂ© quelque peu déçu par la documentation de construction, qui pourrait ĂȘtre plus dĂ©taillĂ©e. Il dit que vous avez besoin de "Qt 5.2 ou version ultĂ©rieure" pour construire le projet, mais cela nĂ©cessitait en fait quelques packages spĂ©cifiques, ce qui n'Ă©tait pas facile Ă comprendre Ă partir du journal CMake. Soit dit en passant, il est considĂ©rĂ© de nos jours comme une bonne pratique d'inclure un Dockerfile dans le projet pour faciliter la configuration de l'environnement de dĂ©veloppement par l'utilisateur.
Voici la sortie de l'utilitaire Cloc montrant comment SpeedCrunch se compare aux autres calculatrices:
Revues de bugs pour les autres projets:
L'analyse a été effectuée avec l'analyseur statique
PVS-Studio . Il s'agit d'un ensemble de solutions pour le contrÎle qualité des logiciels et la détection des bogues et des vulnérabilités potentielles. PVS-Studio prend en charge C, C ++, C # et Java et s'exécute sur Windows, Linux et macOS.
Logique étrange dans une boucle
V560 Une partie de l'expression conditionnelle est toujours vraie:! RuleFound. evaluator.cpp 1410
void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false;
Notez la variable
ruleFound : elle est définie sur false à chaque itération. Dans le corps de la boucle, cependant, cette variable est définie sur true dans certaines conditions, mais elle sera redéfinie sur false à la prochaine itération. La variable
ruleFound aurait probablement dĂ» ĂȘtre dĂ©clarĂ©e avant la boucle.
Comparaisons suspectes
V560 Une partie de l'expression conditionnelle est toujours vraie: m_scrollDirection! = 0. resultdisplay.cpp 242
void ResultDisplay::fullContentScrollEvent() { QScrollBar* bar = verticalScrollBar(); int value = bar->value(); bool shouldStop = (m_scrollDirection == -1 && value <= 0) || (m_scrollDirection == 1 && value >= bar->maximum()); if (shouldStop && m_scrollDirection != 0) {
Si la
valeur de la variable
shouldStop est
vraie , la variable
m_scrollDirection prendra l'une des deux valeurs: -1 ou 1. Par conséquent, sa valeur sera certainement différente de zéro dans la prochaine instruction conditionnelle, ce à quoi l'analyseur met en garde.
V668 Il est inutile de tester le pointeur 'item' sur null, car la mémoire a été allouée à l'aide de l'opérateur 'new'. L'exception sera générée en cas d'erreur d'allocation de mémoire. editor.cpp 998
void EditorCompletion::showCompletion(const QStringList& choices) { .... for (int i = 0; i < choices.count(); ++i) { QStringList pair = choices.at(i).split(':'); QTreeWidgetItem* item = new QTreeWidgetItem(m_popup, pair); if (item && m_editor->layoutDirection() == Qt::RightToLeft) item->setTextAlignment(0, Qt::AlignRight); .... } .... }
La mémoire d'un objet de type
QTreeWidgetItem est allouée à l'aide du
nouvel opérateur. Cela signifie qu'un échec d'allocation de mémoire entraßnera le lancement d'une exception
std :: bad_alloc () . La vérification du pointeur d'
Ă©lĂ©ment est donc redondante et peut ĂȘtre supprimĂ©e.
Déréférence NULL potentielle
V595 Le pointeur «ioparams» a Ă©tĂ© utilisĂ© avant d'ĂȘtre vĂ©rifiĂ© par rapport Ă nullptr. VĂ©rifiez les lignes: 969, 983. floatio.c 969
int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase;
Le pointeur
ioparams est dĂ©rĂ©fĂ©rencĂ© avant la vĂ©rification. Il semble qu'il y ait une erreur ici. Ătant donnĂ© que la dĂ©rĂ©fĂ©rence est prĂ©cĂ©dĂ©e d'un certain nombre de conditions, le bogue n'apparaĂźtra pas souvent, mais il aura un effet drastique lorsqu'il le fera.
Division par zéro
V609 Divisez par zéro. Plage du dénominateur [0..4]. floatconvert.c 266
static int lgbase( signed char base) { switch(base) { case 2: return 1; case 8: return 3; case 16: return 4; } return 0;
La fonction
lgbase peut retourner zĂ©ro, qui pourrait ensuite ĂȘtre utilisĂ© comme diviseur. La fonction peut ĂȘtre potentiellement appelĂ©e avec n'importe quelle valeur, pas seulement 2, 8 ou 16.
Comportement indéfini
V610 Comportement
indéfini . Vérifiez l'opérateur de décalage «<<». L'opérande gauche '(~ 0)' est négatif. floatlogic.c 64
static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT;
Ătant donnĂ© que le rĂ©sultat de l'inversion de zĂ©ro est stockĂ© dans un
entier signé, la valeur résultante sera un nombre négatif, qui est ensuite décalé. Décaler à gauche une valeur négative est un comportement non défini.
Voici une liste complĂšte de tous ces cas:
- V610 Comportement indéfini. Vérifiez l'opérateur de décalage «<<». L'opérande gauche '(- 1)' est négatif. floatnum.c 289
- V610 Comportement indéfini. Vérifiez l'opérateur de décalage «<<». L'opérande gauche '(- 1)' est négatif. floatnum.c 325
- V610 Comportement indéfini. Vérifiez l'opérateur de décalage «<<». L'opérande gauche '(- 1)' est négatif. floatnum.c 344
- V610 Comportement indéfini. Vérifiez l'opérateur de décalage «<<». L'opérande gauche '(- 1)' est négatif. floatnum.c 351
Balises HTML non fermées
V735 Peut-ĂȘtre un HTML incorrect. La balise de fermeture "</body>" a Ă©tĂ© rencontrĂ©e, tandis que la balise "</div>" Ă©tait attendue. book.cpp 127
static QString makeAlgebraLogBaseConversionPage() { return BEGIN INDEX_LINK TITLE(Book::tr("Logarithmic Base Conversion")) FORMULA(y = log(x) / log(a), log<sub>a</sub>x = log(x) / log(a)) END; }
Comme c'est souvent le cas avec le code C / C ++, l'Ă©tude de la source n'aide pas beaucoup Ă comprendre les choses, nous allons donc jeter un Ćil au code prĂ©traitĂ© Ă la place:

L'analyseur a détecté une étiquette
div non fermée. Ce fichier contient de nombreux extraits en HTML, et les développeurs devront également vérifier ce code.
Voici quelques autres cas suspects trouvés par PVS-Studio:
- V735 Peut-ĂȘtre un HTML incorrect. La balise de fermeture "</td>" a Ă©tĂ© rencontrĂ©e, tandis que la balise "</sub>" Ă©tait attendue. book.cpp 344
- V735 Peut-ĂȘtre un HTML incorrect. La balise de fermeture "</td>" a Ă©tĂ© rencontrĂ©e, tandis que la balise "</sub>" Ă©tait attendue. book.cpp 347
Opérateur d'affectation
V794 L'opĂ©rateur d'affectation doit ĂȘtre protĂ©gĂ© contre le cas de 'this == & other'. quantitĂ©.cpp 373
Quantity& Quantity::operator=(const Quantity& other) { m_numericValue = other.m_numericValue; m_dimension = other.m_dimension; m_format = other.m_format; stripUnits(); if(other.hasUnit()) { m_unit = new CNumber(*other.m_unit); m_unitName = other.m_unitName; } cleanDimension(); return *this; }
Il est recommandĂ© de vĂ©rifier les situations dans lesquelles un objet est attribuĂ© Ă lui-mĂȘme en comparant les pointeurs. En d'autres termes, ajoutez les deux lignes suivantes au dĂ©but du corps de la fonction:
if (this == &other) return *this;
Pour rappel
V601 La valeur 'false' est implicitement
convertie en type entier. cmath.cpp 318
int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false;
Parfois, vous dites dans les commentaires que certains avertissements sont peut-ĂȘtre dĂ©clenchĂ©s par un code incomplet. Oui, cela se produit de temps en temps, mais nous signalons spĂ©cifiquement de tels cas.
Conclusion
Nous avons dĂ©jĂ vĂ©rifiĂ© le code de trois programmes de calculatrice - Windows Calculator, Qalculate! Et SpeedCrunch - et nous n'allons pas nous arrĂȘter. N'hĂ©sitez pas Ă suggĂ©rer des projets que vous souhaitez que nous vĂ©rifions car les classements logiciels ne reflĂštent pas toujours l'Ă©tat rĂ©el des choses.
Bienvenue pour télécharger
PVS-Studio et l'essayer sur votre propre «calculatrice». :-)