¡Aquí estamos, continuando explorando el código de las calculadoras! Hoy vamos a echar un vistazo al proyecto llamado SpeedCrunch, la segunda calculadora gratuita más popular.
Introduccion
SpeedCrunch es una calculadora científica de alta precisión que presenta una interfaz de usuario rápida y controlada por teclado. Es un software gratuito y de código abierto, con licencia bajo la GPL y que se ejecuta en Windows, Linux y macOS.
El código fuente está disponible en
BitBucket . Me decepcionó un poco la documentación de compilación, que podría ser más detallada. Dice que necesita "Qt 5.2 o posterior" para construir el proyecto, pero en realidad requiere algunos paquetes específicos, lo que no fue fácil de deducir del registro de CMake. Por cierto, hoy en día se considera una buena práctica incluir un Dockerfile en el proyecto para que sea más fácil para el usuario configurar el entorno de desarrollo.
Aquí está la salida de la utilidad Cloc que muestra cómo SpeedCrunch se compara con otras calculadoras:
Revisiones de errores para los otros proyectos:
El análisis se realizó con el analizador estático
PVS-Studio . Este es un paquete de soluciones para el control de calidad del software y la detección de errores y vulnerabilidades potenciales. PVS-Studio es compatible con C, C ++, C # y Java, y se ejecuta en Windows, Linux y macOS.
Extraña lógica en un bucle
V560 Una parte de la expresión condicional siempre es cierta :! RuleFound. evaluator.cpp 1410
void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false;
Tenga en cuenta la variable
ruleFound : se establece en falso en cada iteración. Sin embargo, dentro del cuerpo del bucle, esa variable se establece en verdadero en ciertas condiciones, pero se volverá a establecer en falso en la próxima iteración. La variable
ruleFound probablemente debería haberse declarado antes del bucle.
Comparaciones sospechosas
V560 Una parte de la expresión condicional siempre es verdadera: 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 el
valor de la variable
shouldStop es
verdadero , entonces la variable
m_scrollDirection tomará uno de los dos valores: -1 o 1. Por lo tanto, su valor definitivamente será diferente de cero en la siguiente declaración condicional, que es sobre lo que el analizador está advirtiendo.
V668 No tiene sentido probar el puntero 'item' contra nulo, ya que la memoria fue asignada usando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. 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 memoria para un objeto de tipo
QTreeWidgetItem se asigna utilizando el
nuevo operador. Significa que una falla de asignación de memoria conducirá a lanzar una excepción
std :: bad_alloc () . Comprobar el puntero del
elemento es, por lo tanto, redundante y se puede eliminar.
Desreferencia NULL potencial
V595 El puntero 'ioparams' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 969, 983. floatio.c 969
int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase;
El puntero
ioparams se desreferencia antes de la verificación. Parece que hay algún error aquí. Dado que la desreferencia está precedida por una serie de condiciones, el error no aparecerá con frecuencia, pero tendrá un efecto drástico cuando lo haga.
División por cero
V609 Divide por cero. Rango del denominador [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 función
lgbase puede devolver cero, que luego podría usarse como divisor. La función se puede llamar potencialmente con cualquier valor, no solo 2, 8 o 16.
Comportamiento indefinido
V610 Comportamiento indefinido. Verifique el operador de turno '<<'. El operando izquierdo '(~ 0)' es negativo. floatlogic.c 64
static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT;
Debido a que el resultado de invertir cero se almacena en un
int con signo, el valor resultante será un número negativo, que luego se desplaza. Desplazar a la izquierda un valor negativo es un comportamiento indefinido.
Aquí hay una lista completa de todos estos casos:
- V610 Comportamiento indefinido. Verifique el operador de turno '<<'. El operando izquierdo '(- 1)' es negativo. floatnum.c 289
- V610 Comportamiento indefinido. Verifique el operador de turno '<<'. El operando izquierdo '(- 1)' es negativo. floatnum.c 325
- V610 Comportamiento indefinido. Verifique el operador de turno '<<'. El operando izquierdo '(- 1)' es negativo. floatnum.c 344
- V610 Comportamiento indefinido. Verifique el operador de turno '<<'. El operando izquierdo '(- 1)' es negativo. floatnum.c 351
Etiquetas HTML no cerradas
V735 Posiblemente un HTML incorrecto. Se encontró la etiqueta de cierre "</body>", mientras que se esperaba la etiqueta "</div>". 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; }
Como suele ser el caso con el código C / C ++, el estudio de la fuente no ayuda mucho a descifrar las cosas, por lo que echaremos un vistazo al código preprocesado:

El analizador ha detectado una etiqueta
div no
cerrada . Este archivo contiene muchos fragmentos en HTML, y los desarrolladores también deberán verificar ese código.
Aquí hay un par de otros casos sospechosos encontrados por PVS-Studio:
- V735 Posiblemente un HTML incorrecto. Se encontró la etiqueta de cierre "</td>", mientras que se esperaba la etiqueta "</sub>". book.cpp 344
- V735 Posiblemente un HTML incorrecto. Se encontró la etiqueta de cierre "</td>", mientras que se esperaba la etiqueta "</sub>". book.cpp 347
Operador de asignación
V794 El operador de asignación debe estar protegido del caso de 'this == & other'. cantidad.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; }
Se recomienda que compruebe situaciones en las que un objeto se asigna a sí mismo comparando los punteros. En otras palabras, agregue las siguientes dos líneas al comienzo del cuerpo de la función:
if (this == &other) return *this;
Como recordatorio
V601 El valor 'falso' se
convierte implícitamente en el tipo entero. cmath.cpp 318
int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false;
Algunas veces usted dice en los comentarios que quizás algunas de las advertencias son activadas por un código incompleto. Sí, eso sucede de vez en cuando, pero señalamos específicamente tales casos.
Conclusión
Ya hemos verificado el código de tres programas de calculadora: Windows Calculator, Qalculate! Y SpeedCrunch, y no vamos a parar. No dude en sugerir proyectos que desea que verifiquemos porque las clasificaciones de software no siempre reflejan el estado real de las cosas.
Bienvenido a descargar
PVS-Studio y probarlo en su propia "Calculadora". :-)