Wie kann man eine Warnung auslösen, wenn der Rückgabewert ignoriert wird?
Lesezeit: 6 Minuten
Drakoscha
Ich möchte alle Stellen in meinem Code (C++) sehen, die den Rückgabewert einer Funktion ignorieren. Wie kann ich das machen – mit gcc oder statischem Code-Analyse-Tool?
Beispiel für schlechten Code:
int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here I disregard the return value
return 1;
}
Bitte beachte, dass:
es sollte auch funktionieren, wenn sich die Funktion und ihre Verwendung in verschiedenen Dateien befinden
kostenlos statisches Prüfwerkzeug
Dadurch werden viele Warnungen gedruckt, wenn Sie verwenden printf zum Beispiel.
– Alok Singhal
11. Januar 10 um 15:49 Uhr
Der “wahrscheinliche” Grund, warum es nicht über die Befehlszeile erzwungen werden kann, ist, dass Sie, wenn Sie einen triftigen Grund haben, ein Ergebnis zu ignorieren, am Ende eine “unbenutzte Variable” zuweisen müssten, die eine Warnung erzeugen würde. Zum Beispiel wollen Sie sicherlich nicht T& operator=(T rhs); um dich zu zwingen, das Ergebnis zu erfassen 😉
– Matthias M.
11. Januar 10 um 20:11 Uhr
@Matthieu M: Sie müssen keine Dummy-Variable zuweisen, wenn Sie den Rückgabewert einer Funktion ignorieren möchten. Wandeln Sie den Funktionsaufruf einfach in void um, wie in (void) function_returning_a_val();. Beim Lesen des Codes wird dadurch auch deutlicher, dass Sie den Rückgabewert bewusst ignorieren.
– bta
15. Januar 10 um 0:01 Uhr
[[nodiscard]] in C++17.
– Marc Glisse
28. Oktober 16 um 16:57 Uhr
Da dies wie die älteste und am meisten positiv bewertete Frage zu diesem Thema aussieht, habe ich eine Update-Antwort zu C++17 hinzugefügt.
– Shafik Yaghmour
22. Juli 18 um 15:49 Uhr
Erich Seppanen
Sie wollen GCCs warn_unused_result Attribut:
#define WARN_UNUSED __attribute__((warn_unused_result))
int WARN_UNUSED f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here i disregard the return value
return 1;
}
Der Versuch, diesen Code zu kompilieren, erzeugt:
$ gcc test.c
test.c: In function `main':
test.c:16: warning: ignoring return value of `f', declared with
attribute warn_unused_result
Sie können dies im Einsatz sehen Linux Kernel; Sie haben ein __must_check Makro, das dasselbe tut; Anscheinend benötigen Sie GCC 3.4 oder höher, damit dies funktioniert. Dann finden Sie dieses Makro, das in Kernel-Header-Dateien verwendet wird:
unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
Funktioniert gut in einer Header-Datei für mich. Ich habe einen Funktionsprototyp mit WARN_UNUSED in eine neue Datei lib.h eingefügt und diesen dann aus test.c eingefügt und dieselbe Warnung erhalten. Beachten Sie auch, dass der Linux-Kernel dies so macht.
– Eric Seppanen
12. Januar 10 um 15:45 Uhr
Weitere Details zu diesem Attribut finden Sie in den offiziellen gcc-Dokumenten. 6.33.1 Common Function Attributes --------------------------------- . Installieren gcc-doc apt-Paket zur einfachen Navigation in gcc-Dokumenten. Sobald Sie es haben, verwenden Sie einfach info zum Lesen $ info gcc und in der Indexsuche nach warn_unused_result.
Das Attribut-Token nodiscard kann auf die Deklarator-ID in einer Funktionsdeklaration oder auf die Deklaration einer Klasse oder Aufzählung angewendet werden. Es darf höchstens einmal in jeder Attributliste vorkommen und es darf keine Attribut-Argument-Klausel vorhanden sein.
und
[Beispiel:[ Example:
struct [[nodiscard]] error_info { /* ... */ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
enable_missile_safety_mode(); // warning encouraged
launch_missiles();
}
error_info &foo();
void f() { foo(); } // warning not encouraged: not a nodiscard call, because neither
// the (reference) return type nor the function is declared nodiscard
[[nodiscard]] int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); // now we obtain a diagnostic
return 1;
}
Wir erhalten jetzt eine Diagnose mit gcc und clang zB
warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
f(i); // now we obtain a diagnostic
^ ~
Soweit mir bekannt ist, gibt es keine GCC-Option, um diese Warnung auszugeben. Wenn Sie jedoch an bestimmten Funktionen interessiert sind, können Sie diese mit einem Attribut taggen:
int fn() __attribute__((warn_unused_result));
was eine Warnung ausgeben würde, wenn der Rückgabewert von fn() nicht verwendet würde. Vorbehalt: Ich habe diese Funktion selbst noch nie verwendet.
Jeder statische Analysecode (z PC-Lint) sollte dir das sagen können. Bei PC-Lint weiß ich, dass dies der Fall ist.
Es ist eine nette Idee. Leider verstößt es gegen das Prinzip “Destruktoren dürfen nicht werfen”: parashift.com/c++-faq-lite/exceptions.html#faq-17.3, aber vielleicht wäre das für einen Testmodus “alle Rückgaben prüfen” akzeptabel (es besteht wahrscheinlich keine Notwendigkeit für die Prüfung in einem endgültigen Release-Build).
– uhrtag
11. Januar 10 um 23:01 Uhr
Ich habe Implementierungen gesehen, die das höchstwertige Bit des Fehlercodes anstelle von verwenden bool.
– Alexander Ach
10. Oktober 14 um 15:55 Uhr
Alon
Ein statischer Analysator wird die Arbeit für Sie erledigen, aber wenn Ihre Codebasis mehr als trivial ist, bereiten Sie sich darauf vor, überwältigt zu werden 😉
bta
Ein statischer Analysator ist hier die beste Wahl. Wir verwenden hier Coverity, aber es gibt sie kostenlose Tools vorhanden, die Sie ebenfalls verwenden können.
Wenn Sie eine Quick-and-Dirty-Lösung benötigen und eine Shell im Linux-Stil zur Hand haben, können Sie Folgendes versuchen:
grep -rn "function_name" * | grep -v "="
Dadurch wird jede Zeile gefunden, die auf die angegebene Funktion verweist, aber kein “=” enthält. Sie können viele falsch positive Ergebnisse (und möglicherweise einige falsch negative) erhalten, aber wenn Sie keinen statischen Analysator haben, ist dies ein anständiger Ausgangspunkt.
Jonathan Leffler
Das klassische ‘lint’-Programm war früher sehr redselig über Funktionen, die einen Wert zurückgaben, der ignoriert wurde. Das Problem war, dass viele dieser Warnungen unerwünscht waren – was zu übermäßigem Rauschen in der Flusenausgabe führte (es nahm Flusen auf, die Sie ignorieren wollten). Das ist wahrscheinlich der Grund, warum GCC keine Standardwarnung dafür hat.
Das andere Problem – die Kehrseite – ist “Wie unterdrücken Sie die Warnung, wenn Sie wissen, dass Sie das Ergebnis ignorieren, sich aber wirklich nicht darum kümmern”. Das klassische Szenario dafür ist:
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, sighandler);
Sie kümmern sich um das erste Ergebnis aus signal(); Sie wissen, dass der zweite SIG_IGN sein wird (da Sie ihn gerade darauf eingestellt haben). Um von den Warnungen wegzukommen, verwende ich manchmal eine Variante von:
if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
old = signal(SIGHUP, sighandler);
Dies ordnet zu old beide Male. Sie können dem mit ‘assert(old == SIG_IGN)’ folgen.
Eigentlich ist die typische Art zu sagen “Nein, der Rückgabewert ist mir wirklich egal” mit einem Cast to void, z.B (void)printf("Hello, world!n"); Auch bei der höchsten Warnstufe wird hierfür mit dem expliziten Cast to void keine Warnung ausgegeben.
– Adam Rosenfield
11. Januar 10 um 16:52 Uhr
Ja das ‘(void)‘ Cast funktioniert … es sieht im Code jedoch hässlich aus. Und ich muss mich um Code kümmern, der dieses hässliche Gramm durch ‘ ersetztVOID‘ stattdessen; es wird abgebildet auf ‘(void)‘ mit Standard-C-Compilern (heutzutage alle C-Compiler) gecastet wurden, aber ihren Ursprung in den Tagen hatten, bevor sie Standard waren, und dann Dinge taten, indem sie das Ergebnis einer statischen Dateivariablen zuwiesen (‘#define VOID _void_ =‘ oder so ungefähr). Aaah; die Erinnerungen und die Reifen, durch die gesprungen werden musste.
– Jonathan Leffler
11. Januar 10 um 23:25 Uhr
.
7586700cookie-checkWie kann man eine Warnung auslösen, wenn der Rückgabewert ignoriert wird?yes
Dadurch werden viele Warnungen gedruckt, wenn Sie verwenden
printf
zum Beispiel.– Alok Singhal
11. Januar 10 um 15:49 Uhr
Der “wahrscheinliche” Grund, warum es nicht über die Befehlszeile erzwungen werden kann, ist, dass Sie, wenn Sie einen triftigen Grund haben, ein Ergebnis zu ignorieren, am Ende eine “unbenutzte Variable” zuweisen müssten, die eine Warnung erzeugen würde. Zum Beispiel wollen Sie sicherlich nicht
T& operator=(T rhs);
um dich zu zwingen, das Ergebnis zu erfassen 😉– Matthias M.
11. Januar 10 um 20:11 Uhr
@Matthieu M: Sie müssen keine Dummy-Variable zuweisen, wenn Sie den Rückgabewert einer Funktion ignorieren möchten. Wandeln Sie den Funktionsaufruf einfach in void um, wie in
(void) function_returning_a_val();
. Beim Lesen des Codes wird dadurch auch deutlicher, dass Sie den Rückgabewert bewusst ignorieren.– bta
15. Januar 10 um 0:01 Uhr
[[nodiscard]]
in C++17.– Marc Glisse
28. Oktober 16 um 16:57 Uhr
Da dies wie die älteste und am meisten positiv bewertete Frage zu diesem Thema aussieht, habe ich eine Update-Antwort zu C++17 hinzugefügt.
– Shafik Yaghmour
22. Juli 18 um 15:49 Uhr