¡El estudio del código de la calculadora continúa! En esta revisión, se revisará el proyecto SpeedCrunch, el segundo más popular entre las calculadoras gratuitas.
Introduccion
SpeedCrunch es una calculadora científica basada en teclado de alta precisión con una interfaz de usuario rápida. Este es un software gratuito de código abierto disponible en Windows, Linux y macOS.
El código fuente está alojado en
BitBucket . Realmente no me gustó la documentación de ensamblaje, que, en mi opinión, debería escribirse con más detalle. Los requisitos especifican "Qt 5.2 o posterior", aunque se necesitaban varios paquetes específicos, que no eran fáciles de aprender del registro de CMake. Por cierto, ahora es una buena práctica aplicar un Dockerfile a un proyecto para configurar rápidamente el entorno de desarrollador deseado.
Para comparar con otras calculadoras, traigo la salida de la utilidad Cloc:
Revisiones de errores en otros proyectos:
PVS-Studio se utilizó como herramienta de análisis estático. Este es un conjunto de soluciones para el control de calidad del código, la búsqueda de errores y posibles vulnerabilidades. Los lenguajes compatibles incluyen: C, C ++, C # y Java. El analizador se puede iniciar 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 . En cada iteración, se establece en falso. Pero si observa el cuerpo de todo el ciclo, entonces, bajo ciertas condiciones, esta variable se establece como verdadera, pero no se tendrá en cuenta en la nueva iteración del ciclo. Lo más probable es que la variable
ruleFound necesitara ser declarada antes del ciclo.
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 la variable
shouldStop es
verdadera , entonces la variable
m_scrollDirection tendrá uno de dos valores: -1 o 1. Por lo tanto, en la siguiente declaración condicional, el valor de la variable
m_scrollDirection definitivamente no será cero, sobre lo cual el analizador advierte.
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. Esto significa que si la asignación de memoria dinámica no es posible, se
generará una excepción
std :: bad_alloc () . Por lo tanto, verificar el puntero del
elemento es superfluo 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 que se verifique su validez. Lo más probable es que se haya introducido un error potencial en el código. Dado que la desreferenciación se realiza bajo varias condiciones, el problema puede manifestarse raramente, pero con precisión.
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 permite que se
devuelva un valor nulo, mediante el cual se realiza la división. Potencialmente, cualquier cosa que no sean los valores 2, 8 y 16 se puede pasar a la función.
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;
El resultado de la inversión de cero se coloca en el tipo de signo
int , por lo que el resultado será un número negativo, para lo cual se realiza un cambio. Desplazar un número negativo a la izquierda es un comportamiento indefinido.
La lista completa de lugares peligrosos:
- 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 suceder con el código C / C ++, no hay nada claro en la fuente, así que pasemos al código preprocesado para este fragmento:

El analizador detectó una etiqueta div no cerrada. Hay muchos fragmentos de código html en este archivo y ahora los desarrolladores deberían comprobarlo adicionalmente.
Aquí hay algunos lugares más sospechosos que se encontraron con 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 considerar la situación cuando el objeto se asigna a sí mismo al comparar los punteros.
En otras palabras, las siguientes dos líneas de código deben agregarse al comienzo del cuerpo de la función:
if (this == &other) return *this;
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;
A veces, en los comentarios a nuestros artículos, sugieren que se emiten algunas advertencias en un código incompleto. Sí, sucede, pero cuando realmente es, se escribe directamente sobre eso.
Conclusión
Revisiones ya disponibles de tres calculadoras: Windows Calculator, Qalculate! y SpeedCrunch. Estamos listos para continuar investigando el código de las calculadoras populares. Puede ofrecer proyectos para verificación, ya que las calificaciones del software no siempre reflejan la imagen real.
Verifique su "Calculadora" descargando
PVS-Studio y probando su proyecto :-)

Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Svyatoslav Razmyslov.
Siguiendo los pasos de las calculadoras: SpeedCrunch