Aqui estamos, continuando a explorar o código das calculadoras! Hoje vamos dar uma olhada no projeto chamado SpeedCrunch, a segunda calculadora gratuita mais popular.
1. Introdução
SpeedCrunch é uma calculadora científica de alta precisão com uma interface de usuário rápida, acionada por teclado. É um software gratuito e de código aberto, licenciado sob a GPL e executando no Windows, Linux e macOS.
O código fonte está disponível no
BitBucket . Fiquei um pouco decepcionado com a documentação da compilação, que poderia ser mais detalhada. Ele diz que você precisa do “Qt 5.2 ou posterior” para criar o projeto, mas na verdade foram necessários alguns pacotes específicos, o que não foi fácil de descobrir no log do CMake. A propósito, atualmente é considerado uma boa prática incluir um Dockerfile no projeto para facilitar ao usuário a configuração do ambiente de desenvolvimento.
Aqui está o resultado do utilitário Cloc, mostrando como o SpeedCrunch se compara a outras calculadoras:
Revisões de erros para os outros projetos:
A análise foi realizada com o analisador estático
PVS-Studio . Este é um pacote de soluções para controle de qualidade de software e detecção de bugs e possíveis vulnerabilidades. O PVS-Studio suporta C, C ++, C # e Java e é executado no Windows, Linux e macOS.
Lógica estranha em um loop
V560 Uma parte da expressão condicional é sempre verdadeira :! RuleFound. assessator.cpp 1410
void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false;
Observe a variável
ruleFound : ela é definida como false a cada iteração. No interior do corpo do loop, porém, essa variável é configurada como true em determinadas condições, mas será configurada novamente como false na próxima iteração. A variável
ruleFound provavelmente deveria ter sido declarada antes do loop.
Comparações suspeitas
V560 Uma parte da expressão condicional é sempre verdadeira: 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) {
Se o valor da variável
shouldStop for
verdadeiro , a variável
m_scrollDirection assumirá um dos dois valores: -1 ou 1. Portanto, seu valor será definitivamente diferente de zero na próxima instrução condicional, sobre a qual o analisador está alertando.
V668 Não há sentido em testar o ponteiro 'item' contra nulo, pois a memória foi alocada usando o operador 'novo'. A exceção será gerada no caso de erro de alocação de memória. 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); .... } .... }
A memória para um objeto do tipo
QTreeWidgetItem é alocada usando o
novo operador. Isso significa que uma falha na alocação de memória levará ao lançamento de uma exceção
std :: bad_alloc () . A verificação do ponteiro do
item é, portanto, redundante e pode ser removida.
Desreferência potencial NULL
V595 O ponteiro 'ioparams' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 969, 983. floatio.c 969
int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase;
O ponteiro
ioparams é desreferenciado antes da verificação. Parece que há algum erro aqui. Como a desreferência é precedida por várias condições, o bug não será exibido com frequência, mas terá um efeito drástico quando ocorrer.
Divisão por zero
V609 Divida por zero. Intervalo do 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;
A função
lgbase pode retornar zero, que pode ser usada como um divisor. A função pode ser chamada potencialmente com qualquer valor, não apenas 2, 8 ou 16.
Comportamento indefinido
V610 Comportamento indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(~ 0)' é negativo. floatlogic.c 64
static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT;
Como o resultado da inversão de zero é armazenado em um
int assinado, o valor resultante será um número negativo, que é então deslocado. Mudar para a esquerda um valor negativo é um comportamento indefinido.
Aqui está uma lista completa de todos esses casos:
- V610 Comportamento indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(- 1)' é negativo. floatnum.c 289
- V610 Comportamento indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(- 1)' é negativo. floatnum.c 325
- V610 Comportamento indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(- 1)' é negativo. floatnum.c 344
- V610 Comportamento indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(- 1)' é negativo. floatnum.c 351
Tags HTML não fechadas
V735 Possivelmente um HTML incorreto. A tag de fechamento "</body>" foi encontrada, enquanto a tag "</div>" era esperada. 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 costuma ser o caso do código C / C ++, estudar a fonte não ajuda muito a entender as coisas; portanto, veremos o código pré-processado:

O analisador detectou uma etiqueta
div não fechada. Este arquivo contém muitos trechos em HTML, e os desenvolvedores também terão que verificar esse código.
Aqui estão alguns outros casos suspeitos encontrados pelo PVS-Studio:
- V735 Possivelmente um HTML incorreto. A tag de fechamento "</td>" foi encontrada, enquanto a tag "</sub>" era esperada. book.cpp 344
- V735 Possivelmente um HTML incorreto. A tag de fechamento "</td>" foi encontrada, enquanto a tag "</sub>" era esperada. book.cpp 347
Operador de atribuição
V794 O operador de atribuição deve ser protegido do caso 'this == & other'. Quantity.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; }
É recomendável que você verifique situações em que um objeto é atribuído a si mesmo, comparando os ponteiros. Em outras palavras, adicione as duas linhas a seguir no início do corpo da função:
if (this == &other) return *this;
Como lembrete
V601 O valor 'false' é convertido implicitamente no tipo inteiro. cmath.cpp 318
int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false;
Às vezes, você diz nos comentários que talvez alguns dos avisos sejam acionados por código incompleto. Sim, isso acontece de vez em quando, mas apontamos especificamente esses casos.
Conclusão
Já verificamos o código de três programas da calculadora - Windows Calculator, Qalculate! E SpeedCrunch - e não vamos parar. Sinta-se à vontade para sugerir projetos que você deseja que analisemos, porque as classificações de software nem sempre refletem o estado real das coisas.
Bem-vindo ao baixar o
PVS-Studio e tente por conta própria “Calculadora”. :-)