Inilah kami, terus mengeksplorasi kode kalkulator! Hari ini kita akan melihat proyek bernama SpeedCrunch, kalkulator gratis terpopuler kedua.
Pendahuluan
SpeedCrunch adalah kalkulator ilmiah presisi tinggi yang menampilkan antarmuka pengguna yang digerakkan oleh keyboard. Ini adalah perangkat lunak bebas dan sumber terbuka, dilisensikan di bawah GPL dan berjalan di Windows, Linux, dan macOS.
Kode sumber tersedia di
BitBucket . Saya agak kecewa dengan dokumentasi pembangunan, yang bisa lebih detail. Dikatakan bahwa Anda perlu βQt 5.2 atau yang lebih baruβ untuk membangun proyek, tetapi sebenarnya dibutuhkan beberapa paket spesifik, yang tidak mudah diketahui dari log CMake. Ngomong-ngomong, ini dianggap praktik yang baik saat ini untuk memasukkan Dockerfile ke dalam proyek untuk membuatnya lebih mudah bagi pengguna untuk mengatur lingkungan pengembangan.
Berikut ini output dari utilitas Cloc yang menunjukkan bagaimana SpeedCrunch membandingkan dengan kalkulator lain:
Ulasan bug untuk proyek-proyek lain:
Analisis dilakukan dengan analisa statis
PVS-Studio . Ini adalah paket solusi untuk kontrol kualitas perangkat lunak dan deteksi bug dan kerentanan potensial. PVS-Studio mendukung C, C ++, C #, dan Java dan berjalan pada Windows, Linux, dan macOS.
Logika aneh dalam satu lingkaran
V560 Bagian dari ekspresi kondisional selalu benar:! RuleFound. evaluator.cpp 1410
void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false;
Perhatikan variabel
ruleFound : diset ke false di setiap iterasi. Di dalam tubuh loop, meskipun, variabel itu disetel ke true pada kondisi tertentu, tetapi itu akan diatur kembali ke false pada iterasi berikutnya. Variabel
ruleFound seharusnya dideklarasikan sebelum loop.
Perbandingan yang mencurigakan
V560 Bagian dari ekspresi kondisional selalu benar: 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) {
Jika nilai
shouldStop variabel
benar , maka variabel
m_scrollDirection akan mengambil salah satu dari dua nilai: -1 atau 1. Oleh karena itu, nilainya pasti akan berbeda dari nol dalam pernyataan bersyarat berikutnya, yang merupakan apa yang diingatkan oleh penganalisa.
V668 Tidak ada gunanya menguji pointer 'item' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. 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); .... } .... }
Memori untuk objek tipe
QTreeWidgetItem dialokasikan menggunakan operator
baru . Ini berarti bahwa kegagalan alokasi memori akan menyebabkan melempar pengecualian
std :: bad_alloc () . Oleh karena itu, memeriksa penunjuk
item mubazir dan dapat dihapus.
Potensi NULL Dereference
V595 Pointer 'ioparams' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 969, 983. floatio.c 969
int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase;
Pointer
ioparams ditereferensi sebelum cek. Sepertinya ada beberapa kesalahan di sini. Karena dereference didahului oleh sejumlah kondisi, bug tidak akan sering muncul, tetapi itu akan memiliki efek drastis ketika itu terjadi.
Pembagian dengan nol
V609 Bagilah dengan nol. Kisaran penyebut [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;
Fungsi
lgbase dapat mengembalikan nol, yang kemudian dapat digunakan sebagai pembagi. Fungsi ini dapat berpotensi dipanggil dengan nilai apa pun, tidak hanya 2, 8, atau 16.
Perilaku tidak terdefinisi
V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(~ 0)' negatif. floatlogic.c 64
static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT;
Karena hasil pembalik nol disimpan ke
int yang ditandatangani, nilai yang dihasilkan akan menjadi angka negatif, yang kemudian digeser. Menggeser-kiri nilai negatif adalah perilaku yang tidak terdefinisi.
Berikut daftar lengkap semua kasus tersebut:
- V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 289
- V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 325
- V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 344
- V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 351
Tag HTML tidak tertutup
V735 Mungkin HTML yang salah. Tag penutup "</body>" ditemukan, sedangkan tag "</div>" diharapkan. 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; }
Seperti yang sering terjadi dengan kode C / C ++, mempelajari sumber tidak banyak membantu dalam mencari tahu, jadi kita akan melihat pada kode preprocessed sebagai gantinya:

Penganalisis telah mendeteksi tag
div tidak tertutup. File ini mengandung banyak cuplikan dalam HTML, dan pengembang harus memeriksa kode itu juga.
Berikut adalah beberapa kasus mencurigakan lainnya yang ditemukan oleh PVS-Studio:
- V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 344
- V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 347
Operator penugasan
V794 Operator penugasan harus dilindungi dari kasus 'this == & other'. kuantitas.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; }
Dianjurkan agar Anda memeriksa situasi di mana objek ditugaskan untuk dirinya sendiri dengan membandingkan pointer. Dengan kata lain, tambahkan dua baris berikut ke bagian awal badan fungsi:
if (this == &other) return *this;
Sebagai pengingat
V601 Nilai 'salah' secara implisit dilemparkan ke tipe integer. cmath.cpp 318
int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false;
Kadang-kadang Anda mengatakan dalam komentar bahwa mungkin beberapa peringatan dipicu oleh kode yang tidak lengkap. Ya, itu terjadi sesekali, tetapi kami secara khusus menunjukkan kasus-kasus seperti itu.
Kesimpulan
Kami telah memeriksa kode tiga program kalkulator - Windows Calculator, Qalculate!, Dan SpeedCrunch - dan tidak akan berhenti. Jangan ragu untuk menyarankan proyek yang Anda ingin kami periksa karena peringkat perangkat lunak tidak selalu mencerminkan keadaan sebenarnya.
Selamat mengunduh
PVS-Studio dan cobalah "Kalkulator" Anda sendiri. :-)