
Unsere Leserschaft wächst, daher schreiben wir immer wieder Artikel, in denen erklärt wird, wie die statische Code-Analysemethode richtig angewendet wird. Wir halten es für sehr wichtig zu erklären, dass statische Analysewerkzeuge nicht sporadisch, sondern regelmäßig verwendet werden sollten. Wir demonstrieren dies noch einmal anhand eines praktischen Beispiels, indem wir das Godot-Projekt erneut überprüfen.
Verwenden Sie regelmäßig Analysegeräte
Als ich mich darauf vorbereitete, auf der Konferenz der Spieleentwickler zu sprechen, beschloss ich, neue Beispiele für interessante Fehler zu erhalten, die das
PVS-Studio- Tool erkennen kann. Zu diesem Zweck wurden mehrere Spiel-Engines getestet, von denen eine Godot war. Ich habe keine besonders interessanten Fehler für den Bericht gefunden, aber ich wollte einen Artikel über gewöhnliche Fehler schreiben. Diese Fehler zeigen sehr gut die Relevanz der regelmäßigen Verwendung von statischen Code-Analyse-Tools.
Es ist zu beachten, dass wir dieses Projekt bereits 2015
überprüft haben und der Autor mit den Fehlern gearbeitet hat, die wir ausgeschrieben haben.
Hier sehen Sie das entsprechende Commit.
3 Jahre sind vergangen. Das Projekt hat sich geändert. Der PVS-Studio-Analysator wurde ebenfalls geändert, und es wurden neue Diagnosen angezeigt. Daher ist es nicht verwunderlich, dass ich einfach und schnell genug Beispiele für Fehler schreiben konnte, um diesen Artikel zu schreiben.
Es ist jedoch noch etwas anderes wichtig. Bei der Entwicklung von Godot oder einem anderen Projekt treten ständig neue Fehler auf und werden behoben. Nicht erkannte Fehler „setzen“ sich lange Zeit im Code fest, und dann können viele von ihnen beim Ausführen einer statischen Code-Analyse erkannt werden. Aus diesem Grund besteht manchmal das falsche Gefühl, dass statische Analysatoren in selten verwendeten Codeabschnitten nur einige uninteressante Fehler finden. Dies ist natürlich der Fall, wenn Sie den Analysator falsch verwenden und ihn nur von Zeit zu Zeit ausführen, z. B. kurz vor der Veröffentlichung der Version.
Ja, beim Schreiben von Artikeln führen wir selbst einmalige Überprüfungen offener Projekte durch. Aber wir haben ein anderes Ziel. Wir möchten die Fähigkeiten eines Code-Analysators zur Erkennung von Fehlern demonstrieren. Diese Aufgabe hat wenig mit der Verbesserung der Qualität des gesamten Projektcodes und der Reduzierung der mit der Fehlerkorrektur verbundenen Kosten zu tun.
Noch einmal über die Hauptsache. Bei der statischen Code-Analyse geht es nicht darum, alte Fehler zu finden. Diese alten Fehler sind normalerweise unbedeutend, da sie sonst die Benutzer gestört hätten und bereits gefunden und behoben worden wären. Bei der statischen Analyse geht es darum, Fehler in neu geschriebenem oder geändertem Code schnell zu beheben. Dies reduziert die Debugging-Zeit, die Anzahl der Benutzerbeschwerden und letztendlich das Budget des entwickelten Projekts.
Kommen wir nun zu den Fehlern, die die Leser unserer Veröffentlichungen so sehr lieben.
Fehler beim Kopieren und Einfügen
Mal sehen, was mir beim Studium des PVS-Studio-Berichts aufgefallen ist. Ich beginne mit meiner bevorzugten V501-Diagnose, die
in fast jedem Projekt , das wir überprüfen,
Fehler findet :).
Fehler N1virtual bool can_export(....) { .... if (!exists_export_template("uwp_" + platform_infix + "_debug.zip", &err) || !exists_export_template("uwp_" + platform_infix + "_debug.zip", &err)) { valid = false; r_missing_templates = true; } .... }
PVS-Studio-Warnung: V501 CWE-570 Es gibt identische Unterausdrücke '! Exists_export_template ("uwp_" + platform_infix + "_debug.zip", & err)' links und rechts vom '||' Betreiber. export.cpp 1135
Klassischer Fehler beim Kopieren und Einfügen. Der Funktionsaufruf wurde kopiert, aber nicht bearbeitet. Der Name der zweiten zu verarbeitenden Datei muss mit "_release.zip" enden.
Fehler N2, N3 static String dump_node_code(SL::Node *p_node, int p_level) { .... if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW) { code += scode;
PVS-Studio Warnung: V501 CWE-570 Links und rechts vom '||' befinden sich identische Unterausdrücke 'bnode-> Anweisungen [i] -> type == SL :: Node :: TYPE_CONTROL_FLOW'. Betreiber. test_shader_lang.cpp 183
void EditorSpinSlider::_notification(int p_what) { if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT || p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT) { if (grabbing_spinner) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); grabbing_spinner = false; grabbing_spinner_attempt = false; } } .... }
PVS-Studio Warnung: V501 CWE-570 Links und rechts vom '||' befinden sich identische Unterausdrücke 'p_what == MainLoop :: NOTIFICATION_WM_FOCUS_OUT'. Betreiber. editor_spin_slider.cpp 157
Ich denke, die Fehler sind deutlich sichtbar und bedürfen keiner Erklärung. Genau das gleiche klassische Copy-Paste wie im ersten Fall.
Fehler N4 String SoftBody::get_configuration_warning() const { .... Transform t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { if (!warning.empty()) .... }
PVS-Studio Warnung: V501 CWE-570 Links und rechts vom '||' befinden sich identische Unterausdrücke. Betreiber. soft_body.cpp 399
Hier wurde die erste Zeile zweimal kopiert. Die Koordinatenachsennummer wurde jedoch nur in der zweiten Zeile geändert. Und sie haben vergessen, die dritte Zeile zu bearbeiten. Dies ist nichts anderes als der "
Last Line-Effekt ".
Hinweis Im Moment habe ich neben dem „Last Line Effect“ folgende interessante Beobachtungen gemacht: „
Die gefährlichste Funktion in der C / C ++ - Welt “, „Das
Böse lebt in Vergleichsfunktionen “. Und jetzt werde ich einen neuen Artikel ankündigen, dessen Schreiben ich in naher Zukunft planen werde. Der Arbeitstitel lautet "0, 1, 2". Es sollte interessant und lehrreich sein. Ich lade Sie ein, einen der Kanäle zu abonnieren, um nicht zu verpassen:
Twitter ,
vk.com ,
Instagram ,
Telegramm und "Old School"
-Rss .
Fehler N5 void ScrollContainer::_notification(int p_what) { .... if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) size.y -= h_scroll->get_minimum_size().y; if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) size.x -= h_scroll->get_minimum_size().x; .... }
PVS-Studio Warnung: V778 CWE-682 Es wurden zwei ähnliche Codefragmente gefunden. Möglicherweise ist dies ein Tippfehler und die Variable 'v_scroll' sollte anstelle von 'h_scroll' verwendet werden. scroll_container.cpp 249
In Bezug auf diesen Code bin ich mir nicht ganz sicher, ob ein Fehler vorliegt. Ich stimme jedoch dem Analysator zu, dass der zweite Block sehr verdächtig aussieht. Höchstwahrscheinlich wurde der Code mit Copy-Paste geschrieben, und im zweiten Textblock wurde vergessen,
h_scroll durch
v_scroll zu ersetzen.
Der Code sollte wahrscheinlich so sein:
if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) size.y -= h_scroll->get_minimum_size().y; if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) size.x -= v_scroll->get_minimum_size().x;
Fehler N6Ein weiterer Fall, in dem ein ausreichend großes Codefragment kopiert und erfolglos geändert wurde. Die Fehlerzeile ist durch meinen Kommentar "// <=" gekennzeichnet.
void ShaderGLES2::bind_uniforms() { .... const Map<uint32_t, Variant>::Element *E = uniform_defaults.front(); while (E) { int idx = E->key(); int location = version->uniform_location[idx]; if (location < 0) { E = E->next(); continue; } Variant v; v = E->value(); _set_uniform_variant(location, v); E = E->next(); } const Map<uint32_t, CameraMatrix>::Element *C = uniform_cameras.front(); while (C) { int idx = E->key();
PVS-Studio Warnung: V522 CWE-476 Es kann zu einer Dereferenzierung des Nullzeigers 'E' kommen. shader_gles2.cpp 102
Der Fehler wurde indirekt erkannt. Dank der Analyse
des Datenflusses ergab PVS-Studio, dass der Zeiger
E zum Zeitpunkt der Dereferenzierung möglicherweise Null ist.
Der Fehler ist, dass sie im kopierten Codefragment vergessen haben,
E in
C an einer Stelle zu ersetzen. Aufgrund dieses Fehlers arbeitet die Funktion auf sehr seltsame Weise und macht seltsame Dinge.
Tippfehler
Fehler N7Für Programmierer, die in anderen Sprachen als C oder C ++ schreiben, ist es schwierig, sich vorzustellen, dass ein Tippfehler durch Schreiben eines Kommas anstelle eines Sterns * gemacht werden kann, und der Code wird kompiliert. Dies ist jedoch so.
LRESULT OS_Windows::WndProc(....) { .... BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = dib_size.x; bmi.bmiHeader.biHeight = dib_size.y; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4; .... }
PVS-Studio Warnung: V521 CWE-480 Solche Ausdrücke mit dem Operator ',' sind gefährlich. Stellen Sie sicher, dass der Ausdruck korrekt ist. os_windows.cpp 776
Der Variablen
bmi.bmiHeader.biSizeImage wird der Wert der Variablen
dib_size.x zugewiesen . Als nächstes wird der Kommaoperator ',' ausgeführt, dessen
Priorität niedriger als die des Operators '=' ist. Das Ergebnis des Ausdrucks
dib_size.y * 4 wird in keiner Weise verwendet.
Anstelle eines Kommas im Ausdruck sollte der Multiplikationsoperator '*' verwendet werden. Erstens wäre ein solcher Ausdruck sinnvoll. Zweitens gibt es unten eine ähnliche, aber bereits korrekte Option zum Initialisieren derselben Variablen:
bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
Fehler N8, N9 void Variant::set(....) { .... int idx = p_index; if (idx < 0) idx += 4; if (idx >= 0 || idx < 4) { Color *v = reinterpret_cast<Color *>(_data._mem); (*v)[idx] = p_value; valid = true; } .... }
PVS-Studio-Warnung: V547 CWE-571 Ausdruck 'idx> = 0 || idx <4 'ist immer wahr. varianten_op.cpp 2152
Jeder Index wird als korrekt angesehen. Um den Fehler zu beheben, müssen Sie das
|| ersetzen am
&& :
if (idx >= 0 && idx < 4) {
Dieser logische Fehler ist höchstwahrscheinlich auf Nachlässigkeit zurückzuführen, daher neige ich dazu, ihn Tippfehlern zuzuschreiben.
Genau der gleiche Fehler kann in der gleichen Datei unten beobachtet werden. Der Fehler des Zuchtfehlers ist anscheinend Copy-Paste.
Mehrfachfehler: V547 CWE-571 Ausdruck 'idx> = 0 || idx <4 'ist immer wahr. varianten_op.cpp 2527
Fehler N10Ein Beispiel für einen Fehler, den man ausrufen möchte: WTF ?!
void AnimationNodeBlendSpace1D::add_blend_point( const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { p_at_index = blend_points_used; } else { for (int i = blend_points_used - 1; i > p_at_index; i++) { blend_points[i] = blend_points[i - 1]; } } .... }
PVS-Studio Warnung: V621 CWE-835 Überprüfen Sie den 'for'-Operator. Es ist möglich, dass die Schleife falsch oder gar nicht ausgeführt wird. animation_blend_space_1d.cpp 113
Beachten Sie die Schleifenstoppbedingung:
i> p_at_index .
Dies ist immer der Fall, da die Variable
i mit dem Wert
blend_points_used - 1 initialisiert wird. Darüber hinaus folgt aus zwei früheren Überprüfungen, dass
blend_points_used> p_at_index ist .
Die Bedingung kann nur dann falsch werden, wenn ein Überlauf der Vorzeichenvariablen
i auftritt, was ein undefiniertes Verhalten ist. Darüber hinaus wird kein Überlauf erreicht, da das Array viel früher über die Grenze hinausgeht.
Vor uns liegt meiner Meinung nach ein wunderschöner Tippfehler, wenn sie '>' anstelle von '<' geschrieben haben. Ja, ich habe eine perverse Sicht auf die Schönheit von Fehlern :).
Der richtige Zyklus:
for (int i = blend_points_used - 1; i < p_at_index; i++) {
Fehler N11Ein weiterer ebenso heller Fall eines Tippfehlers im Zustand des Zyklus.
void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { .... int idx = -1; for (int i = 0; node_rects.size(); i++) { if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } } .... }
PVS-Studio Warnung: V693 CWE-835 Überprüfen Sie den bedingten Ausdruck der Schleife. Es ist möglich, dass 'i <X.size ()' anstelle von 'X.size ()' verwendet wird. animation_state_machine_editor.cpp 852
Ein Überlauf des Arrays kann auftreten, da der Wert von
i unkontrolliert ansteigt. Sicherer Code:
for (int i = 0; i < node_rects.size(); i++) {
Fehler N12 GDScriptDataType GDScriptCompiler::_gdtype_from_datatype( const GDScriptParser::DataType &p_datatype) const { .... switch (p_datatype.kind) { .... case GDScriptParser::DataType::NATIVE: { result.kind = GDScriptDataType::NATIVE; result.native_type = p_datatype.native_type; } break; case GDScriptParser::DataType::SCRIPT: { result.kind = GDScriptDataType::SCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); } case GDScriptParser::DataType::GDSCRIPT: { result.kind = GDScriptDataType::GDSCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); } break; .... }
PVS-Studio Warnung: V796 CWE-484 Möglicherweise fehlt die Anweisung 'break' in der Anweisung switch. gdscript_compiler.cpp 135
Ich habe versehentlich vergessen, eine Pausenerklärung zu schreiben. Wenn es sich also um den
Fall GDScriptParser :: DataType :: SCRIPT handelt, werden den Variablen Werte zugewiesen, als wäre dies der
Fall GDScriptParser :: DataType :: GDSCRIPT .
Fehler N13Der folgende Fehler kann als Kopieren-Einfügen klassifiziert werden. Ich bin mir jedoch nicht sicher, ob eine so kurze Zeile kopiert wurde. Wir werden dies also als einfachen Tippfehler beim Tippen betrachten.
void CPUParticles::_particles_process(float p_delta) { .... if (flags[FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.velocity.z = 0.0; } .... }
PVS-Studio Warnung: V519 CWE-563 Der Variablen 'p.velocity.z' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 664, 665. cpu_particles.cpp 665
Zweimal die Zuordnung derselben Variablen. Unten sehen Sie das folgende Codefragment:
if (flags[FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; }
Höchstwahrscheinlich hätte es für den ersten Fall genauso geschrieben werden müssen.
Fehler N14 bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { if (atlas.is_valid()) { return atlas->is_pixel_opaque( p_x + region.position.x + margin.position.x, p_x + region.position.y + margin.position.y ); } return true; }
PVS-Studio Warnung: V751 Der Parameter 'p_y' wird im Funktionskörper nicht verwendet. textur.cpp 1085
Fragment aus der Beschreibung der
V751- Diagnose:
Der Analysator hat eine verdächtige Funktion festgestellt, deren Parameter niemals verwendet werden. Der andere Parameter wird jedoch mehrmals verwendet, was möglicherweise auf das Vorhandensein eines Fehlers hinweist.Wie Sie sehen können, ist dies in der Tat so und es ist sehr verdächtig. Die Variable
p_x wird zweimal verwendet und
p_y wird nicht verwendet. Höchstwahrscheinlich sollte geschrieben werden:
return atlas->is_pixel_opaque( p_x + region.position.x + margin.position.x, p_y + region.position.y + margin.position.y );
Im Funktionscode ist der Funktionsaufruf übrigens in einer Zeile geschrieben. Aus diesem Grund ist der Fehler schwieriger zu bemerken. Wenn der Autor des Codes die tatsächlichen Argumente in eine Spalte schreiben würde, wie ich es im Artikel getan habe, würde der Fehler sofort meine Aufmerksamkeit auf sich ziehen. Denken Sie daran, dass die Tabellenformatierung sehr nützlich ist und viele Tippfehler vermeidet. Weitere Informationen finden Sie im Kapitel „Richten Sie denselben Codetyp an einer„ Tabelle “im Artikel„
Das Hauptproblem beim Programmieren, Refactoring und so weiter “aus.
Fehler N15 bool SpriteFramesEditor::can_drop_data_fw(....) const { .... Vector<String> files = d["files"]; if (files.size() == 0) return false; for (int i = 0; i < files.size(); i++) { String file = files[0]; String ftype = EditorFileSystem::get_singleton()->get_file_type(file); if (!ClassDB::is_parent_class(ftype, "Texture")) { return false; } } .... }
PVS-Studio Warnung: V767 Verdächtiger Zugriff auf ein Element des 'files'-Arrays durch einen konstanten Index innerhalb einer Schleife. sprite_frames_editor_plugin.cpp 602
Die Schleife verarbeitet bei allen Iterationen der Schleife dieselbe Datei. Tippfehler hier:
String file = files[0];
Muss sein:
String file = files[i];
Andere Fehler
Fehler N16 CSGBrush *CSGBox::_build_brush() { .... for (int i = 0; i < 6; i++) { .... if (i < 3) face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); else face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); .... } .... }
Der PVS-Studio-Analysator gibt sofort zwei Antworten auf diesen Code:
- V547 CWE-570 Der Ausdruck 'i> = 3' ist immer falsch. csg_shape.cpp 939
- V547 CWE-571 Der Ausdruck 'i> = 3' ist immer wahr. csg_shape.cpp 941
In der Tat sieht dieser ternäre Operator in beiden Ausdrücken sehr seltsam aus:
i >= 3 ? -1 : 1
In einem Fall ist die Bedingung immer wahr und in dem anderen ist sie falsch. Schwer zu sagen, wie dieser Code richtig aussehen soll. Vielleicht ist es nur überflüssig und kann so geschrieben werden:
for (int i = 0; i < 6; i++) { .... if (i < 3) face_points[j][(i + k) % 3] = v[k]; else face_points[3 - j][(i + k) % 3] = -v[k]; .... }
Ich kann mich irren und der Code muss auf eine ganz andere Weise repariert werden.
Fehler N17Es gab fast keine Fehler wie V595, obwohl sie normalerweise
in jedem Projekt im Überfluss vorhanden sind . Anscheinend wurden diese Fehler nach einer vorherigen Überprüfung behoben, und dann traten Fehler dieses Typs fast nicht auf. Ich habe nur ein paar Fehlalarme und einen Fehler gesehen.
bool CanvasItemEditor::_get_bone_shape(....) { .... Node2D *from_node = Object::cast_to<Node2D>( ObjectDB::get_instance(bone->key().from)); .... if (!from_node->is_inside_tree()) return false;
PVS-Studio Warnung: V595 CWE-476 Der Zeiger 'from_node' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 565, 567. canvas_item_editor_plugin.cpp 565
Der
from_node- Zeiger
wird zuerst dereferenziert, um die Funktion
is_inside_tree aufzurufen, und erst dann auf
Nullptr- Gleichheit überprüft. Schecks sollten getauscht werden:
if (!from_node) return false; if (!from_node->is_inside_tree()) return false;
Fehler N18 enum JoystickList { .... JOY_AXIS_MAX = 10, .... }; static const char *_axes[] = { "Left Stick X", "Left Stick Y", "Right Stick X", "Right Stick Y", "", "", "L2", "R2" }; int InputDefault::get_joy_axis_index_from_string(String p_axis) { for (int i = 0; i < JOY_AXIS_MAX; i++) { if (p_axis == _axes[i]) { return i; } } ERR_FAIL_V(-1); }
PVS-Studio Warnung: V557 CWE-125 Array-Überlauf ist möglich. Der Wert des 'i'-Index könnte 9 erreichen. Input_default.cpp 1119
Das Array
_axes besteht aus acht Elementen. In diesem Fall beträgt die Konstante
JOY_AXIS_MAX , die die Anzahl der Iterationen der Schleife
festlegt , 10. Es stellt sich heraus, dass die Schleife die Arraygrenze überschreitet.
Fehler N19Und die letzte sehr seltsame Funktion, die anscheinend dazu gedacht ist, etwas zu testen. Die Funktion ist lang, daher gebe ich sie als Bild (zum Vergrößern auf das Bild klicken).
PVS-Studio Warnung: V779 CWE-561 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. test_math.cpp 457
In einer Funktion gibt es mehrere bedingungslose
return-Anweisungen . Auf dem Bild habe ich sie mit roten Ovalen markiert. Es scheint, dass mehrere verschiedene Unit-Tests für diese Funktion gesammelt wurden, aber vergessen haben, die zusätzlichen
Rückgabe-NULL-Werte zu entfernen. Infolgedessen prüft die Funktion nicht, was sie prüfen soll. Fast der gesamte Körper einer Funktion besteht aus nicht erreichbarem Code.
Vielleicht ist das natürlich eine listige Idee. Aber es scheint mir, dass dies zufällig passiert ist und der Code repariert werden sollte.
Lassen Sie uns damit enden. Wenn Sie sich den Bericht des Analysators genau ansehen, können Sie sicherlich andere Fehler finden. Allerdings sogar mehr als genug ausgeschrieben, um einen Artikel zu schreiben. Dann wird es für mich und den Leser langweilig :).
Fazit
Der Artikel beschreibt Fehler, die nicht existieren würden, wenn der Code regelmäßig mit PVS-Studio analysiert würde. Noch wichtiger ist jedoch, dass bei regelmäßiger Analyse viele andere Fehler sofort gefunden und beseitigt werden können. Mein Kollege hat diese Idee in seiner Notiz ausführlicher beschrieben: „
Philosophie der statischen Code-Analyse: Wir haben 100 Programmierer, der Analysator hat nur wenige Fehler gefunden. Ist sie nutzlos? “. Ich empfehle dringend, 10 Minuten mit dem Lesen dieses kurzen, aber sehr wichtigen Artikels zu verbringen.
Vielen Dank für Ihre Aufmerksamkeit. Ich lade alle ein, die statische Analyse von PVS-Studio herunterzuladen und auszuprobieren, um Ihre eigenen Projekte zu testen.

Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov.
Godot: Zur regelmäßigen Verwendung statischer Analysegeräte .