2010-01-11 4 views
44

Ich möchte alle Orte in meinem Code (C++) sehen, die den Rückgabewert einer Funktion ignorieren. Wie kann ich es tun - mit GCC oder statischen Code-Analyse-Tool?Wie wird eine Warnung ausgegeben, wenn der Rückgabewert nicht beachtet wird?

Bad Codebeispiel:

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 beachten Sie, dass:

  • es funktionieren soll, auch wenn die Funktion und ihre Verwendung ist in verschiedenen Dateien
  • kostenlos statische Überprüfung Werkzeug
+3

Dies wird viele Warnungen ausgeben, wenn Sie zum Beispiel 'printf' verwenden. –

+0

Es wird auch auf Ihrem 'Cout' ausgelöst. – Bill

+1

Der "wahrscheinliche" Grund, warum es nicht über die Befehlszeile erzwungen werden kann, besteht darin, dass Sie, wenn Sie einen gültigen Grund haben, ein Ergebnis zu ignorieren, eine "unbenutzte Variable" zuweisen müssen, die eine Warnung generiert. Zum Beispiel wollen Sie sicher nicht, dass T & operator = (T rhs); Sie zwingt, das Ergebnis abzufangen;) –

Antwort

48

Sie wollen warn_unused_result Attribut des GCC:

#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 

du in der Linux kernel im Einsatz sehen können; sie haben einen __must_check Makro, der das Gleiche macht; sieht so aus, als müssten Sie GCC 3.4 oder höher verwenden, damit dies funktioniert. Dann finden Sie das in Kernel-Header-Dateien verwendete Makro:

+0

Ich entferne die Annahme der Antwort, leider funktioniert es nur in einer einzigen Datei. – Drakosha

+0

Haben Sie versucht, es in Header-Datei zu verwenden? –

+2

Funktioniert in einer Header-Datei für mich. Ich habe einen Funktionsprototyp mit WARN_UNUSED in eine neue Datei lib.h gestellt und dann den aus test.c mit der gleichen Warnung versehen. Beachten Sie auch, dass dies der Linux-Kernel tut. –

4

Jeder statische Analysecode (z.B. PC-Lint) sollte Ihnen das sagen können. Für PC-Lint weiß ich, dass dies der Fall ist.

4

ein statischer Analysator die Arbeit für Sie tut, aber wenn Ihre Code-Basis mehr als trivial ist vorbereitet ;-)

10

Was überwältigt werden, wie ich ihm bewusst bin, ist keine GCC Möglichkeit, diese Warnung zu geben . Wenn Sie jedoch in bestimmten Funktionen interessiert sind, können Sie sie mit einem Attribut markieren:

int fn() __attribute__((warn_unused_result)); 

die eine Warnung geben würde, wenn der Rückgabewert von fn() nicht verwendet wurde. Vorbehalt: Ich habe dieses Feature nie selbst benutzt.

4

Ein statischer Analysator ist hier die beste Wahl. Wir verwenden Coverity hier, aber es gibt free tools verfügbar, die Sie auch verwenden können.

Wenn Sie eine schnelle und unsaubere Lösung benötigen und Sie haben ein Linux-basiert Shell handlich, können Sie so etwas wie versuchen:

grep -rn "function_name" * | grep -v "=" 

, dass jede Zeile finden, die die angegebene Funktion verweist aber nicht enthält ein "=". Sie können viele falsche Positive (und möglicherweise einige falsche Negative) erhalten, aber wenn Sie kein statisches Analysegerät haben, ist es ein guter Ausgangspunkt.

10

Sie können diese praktische Vorlage zur Laufzeit verwenden.

Anstatt einen Fehlercode (z. B. HRESULT) zurückzugeben, geben Sie einen return_code <HRESULT> zurück, der behauptet, wenn es den Gültigkeitsbereich verlässt, ohne dass der Wert gelesen wird. Es ist kein statisches Analysewerkzeug, aber trotzdem nützlich.

class return_value 
{ 
public: 
    explicit return_value(T value) 
    :value(value), checked(false) 
    { 
    } 

    return_value(const return_value& other) 
    :value(other.value), checked(other.checked) 
    { 
    other.checked = true; 
    } 

    return_value& operator=(const return_value& other) 
    { 
    if(this != &other) 
    { 
     assert(checked); 
     value = other.value; 
     checked = other.checked; 
     other.checked = true; 
    } 
    } 

    ~return_value(const return_value& other) 
    { 
    assert(checked); 
    } 

    T get_value()const { 
    checked = true; 
    return value; 
    } 

private: 
    mutable bool checked; 
    T value; 
}; 
class return_value 
{ 
public: 
    explicit return_value(T value) 
    :value(value), checked(false) 
    { 
    } 

    return_value(const return_value& other) 
    :value(other.value), checked(other.checked) 
    { 
    other.checked = true; 
    } 

    return_value& operator=(const return_value& other) 
    { 
    if(this != &other) 
    { 
     assert(checked); 
     value = other.value; 
     checked = other.checked; 
     other.checked = true; 
    } 
    } 

    ~return_value(const return_value& other) 
    { 
    assert(checked); 
    } 

    T get_value()const { 
    checked = true; 
    return value; 
    } 

private: 
    mutable bool checked; 
    T value; 
}; 
+0

+1 - das ist eine nette Idee – Bill

+2

Es ist eine nette Idee. Leider verstößt es gegen das Prinzip "Destruktoren dürfen nicht werfen": http://www.parashift.com/c++faq-lite/exceptions.html#faq-17.3, aber vielleicht wäre das akzeptabel für ein "check all returns "Test-Modus (es gibt wahrscheinlich keine Notwendigkeit für die Überprüfung in einem endgültigen Release-Build). – timday

+1

Guter Punkt - Ich habe es geändert, um stattdessen zu behaupten. –

2

Das klassische "Flusen" -Programm war früher sehr viel ü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 man ignorieren wollte). 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, aber wirklich egal". Das klassische Szenario dafür ist:

if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 
    signal(SIGHUP, sighandler); 

Sie interessieren sich für das erste Ergebnis von signal(); Sie wissen, dass die zweite SIG_IGN sein wird (da Sie es nur darauf eingestellt haben). Um weg von den Warnungen, benutze ich manchmal eine Variante auf:

if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 
    old = signal(SIGHUP, sighandler); 

Dies ordnet old beiden Male. Sie können das mit 'assert (old == SIG_IGN)' verfolgen.

+7

Eigentlich ist die typische Art zu sagen "nein, ich interessiere mich wirklich nicht für den Rückgabewert" mit einem Cast zu "void", z. '(void) printf (" Hallo, Welt! \ n ");' Selbst bei der höchsten Warnstufe wird keine Warnung ausgegeben, wenn die explizite Umwandlung in void erfolgt. –

+0

Ja, die '' (void) ''- Cast funktioniert ... sieht aber im Code hässlich aus. Und ich muss nach Code suchen, der dieses hässliche Gramm durch "VOID" ersetzt; Es wird auf ''(void)'' mit Standard-C-Compilern (alle C-Compiler in diesen Tagen) umgewandelt, stammt aber in den Tagen bevor sie Standard waren und hat dann Dinge mit dem Zuordnen des Ergebnisses zu einer statischen Dateivariablen ('#define VOID _void_ = ''oder so ähnlich. Aaah; die Erinnerungen und die Reifen, durch die man springen musste. –

Verwandte Themen