前言
现代编译器具有大量的诊断信息。令人惊讶的是,默认情况下很少启用它们。
在您的这些Internet站点上,以C ++语言提出的大量主张-有关复杂性,不安全性,腿部射击等-恰好是指人们根本不知道可以解决这些问题的情况在键盘上轻弹手指。
让我们修复这种公然的不公正现象,并说明编译器防止错误的能力。
内容
- 颂歌编译器
- 忽略无法解决
- -壁
- -Wextra
- -Wpedantic
- 需要更多警告
- -恐怖
- 结论
- 参考文献
编译器是plus的最好朋友。编译器不仅仅是将人类可读的正式语言翻译成机器代码的工具。编译器是编写程序的最佳助手。
搜索错误是编译器提供的一个重要(并非唯一)帮助。我不是在说错字,类型不匹配以及其他语法错误。我说的是使用警告机制可以捕获的大量错误。
, , , , , "" .. , .
— "" , . — . — , , , , - .
, . , , , . , , . static_cast , , .
, .
, .
, C++ GCC (, Clang). .
-Wall — "" . C++ , ( , , , ).
:
, , .
. , . , :
bool func () {return false;}
int main ()
{
    if (func)
    {
        return 1;
    }
}
prog.cc: In function 'int main()':
prog.cc:5:9: warning: the address of 'bool func()' will never be NULL [-Waddress]
    5 |     if (func)
      |         ^~~~
— . , , :
int main ()
{
    const char * a = "abc";
    if (a == "abc")
    {
        return 0;
    }
}
:
prog.cc: In function 'int main()':
prog.cc:4:11: warning: comparison with string literal results in unspecified behavior [-Waddress]
    4 |     if (a == "abc")
      |         ~~^~~~~~~~
. -O2.
int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    return a[5];
}
prog.cc: In function 'int main()':
prog.cc:4:15: warning: array subscript 5 is above array bounds of 'int [5]' [-Warray-bounds]
    4 |     return a[5];
      |            ~~~^
prog.cc:3:9: note: while referencing 'a'
    3 |     int a[5] = {1, 2, 3, 4, 5};
      |         ^
, :
int main ()
{
    int n = 5;
    if ((n > 1) == 2)
    {
        return 0;
    }
}
prog.cc: In function 'int main()':
prog.cc:5:17: warning: comparison of constant '2' with boolean expression is always false [-Wbool-compare]
    5 |     if ((n > 1) == 2)
      |         ~~~~~~~~^~~~
. , :
int main ()
{
    bool b = true;
    auto c = ~b;
}
prog.cc: In function 'int main()':
prog.cc:4:15: warning: '~' on an expression of type 'bool' [-Wbool-operation]
    4 |     auto c = ~b;
      |               ^
prog.cc:4:15: note: did you mean to use logical not ('!')?
, C++17 , .
, :
struct A
{
    virtual ~A () {};
};
struct B: A{};
int main ()
{
    try {}
    catch (A) {}
}
prog.cc: In function 'int main()':
prog.cc:11:12: warning: catching polymorphic type 'struct A' by value [-Wcatch-value=]
   11 |     catch (A) {}
      |            ^
: -Wcatch-value=n (. ).
, char. char :
int main ()
{
    int a[] = {1, 2, 3, 4};
    char index = 'a' - 'b';
    return a[index];
}
prog.cc: In function 'int main()':
prog.cc:5:14: warning: array subscript has type 'char' [-Wchar-subscripts]
    5 |     return a[index];
      |              ^~~~~
, (/*), , .
int main ()
{
    
    
    rty
}
prog.cc:3:8: warning: "/*" within comment [-Wcomment]
    3 |     /* /* */
      |
prog.cc:4:5: warning: multi-line comment [-Wcomment]
    4 |     //ssd\
      |     ^
, , , :
int main ()
{
    int a = 5;
    if (a <= 4 ? 2 : 3)
    {
        return 0;
    }
}
prog.cc: In function 'int main()':
prog.cc:4:16: warning: ?: using integer constants in boolean context, the expression will always evaluate to 'true' [-Wint-in-bool-context]
    4 |     if (a <= 4 ? 2 : 3)
      |         ~~~~~~~^~~~~~~
— . , , , :
int main ()
{
    for (auto a = 0; 1 << a; a++);
}
prog.cc: In function 'int main()':
prog.cc:3:24: warning: '<<' in boolean context, did you mean '<' ? [-Wint-in-bool-context]
    3 |     for (auto a = 0; 1 << a; a++);
      |                      ~~^~~~
.
int main ()
{
    int a = 5;
    if (a * 5);
}
prog.cc: In function 'int main()':
prog.cc:4:11: warning: '*' in boolean context, suggest '&&' instead [-Wint-in-bool-context]
    4 |     if (a * 5);
      |         ~~^~~
. -Wuninitialized.
int main ()
{
    int i = i;
    return i;
}
prog.cc: In function 'int main()':
prog.cc:3:9: warning: 'i' is used uninitialized in this function [-Wuninitialized]
    3 |     int i = i;
      |         ^
. , .
, :
int main ()
{
    int a = 9;
    if (!a > 1);
}
prog.cc: In function 'int main()':
prog.cc:5:12: warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses]
    5 |     if (!a > 1);
      |            ^
prog.cc:5:9: note: add parentheses around left hand side expression to silence this warning
    5 |     if (!a > 1);
      |         ^~
      |         ( )
, — , .
, .
int main (int argc, const char * argv[])
{
    int x;
    switch (argc)
    {
        case 1: x = 1;
            break;
        case 2: x = 4;
            break;
        case 3: x = 5;
    }
    return x;
}
prog.cc: In function 'int main(int, const char**)':
prog.cc:3:9: warning: 'x' may be used uninitialized in this function [-Wmaybe-uninitialized]
    3 |     int x;
      |         ^
default:
int main (int argc, const char * argv[])
{
    int x;
    switch (argc)
    {
        case 1: x = 1;
            break;
        case 2: x = 4;
            break;
        case 3: x = 5;
            break;
        default:
            x = 17;
    }
    return x;
}
memset, — , — , , .
#include <cstring>
int main ()
{
    constexpr auto size = 20ul;
    int a[size];
    std::memset(a, 0, size);
}
prog.cc: In function 'int main()':
prog.cc:7:27: warning: 'memset' used with length equal to number of elements without multiplication by element size [-Wmemset-elt-size]
    7 |     std::memset(a, 0, size);
      |                           ^
, , , memset:
#include <cstring>
int main ()
{
    constexpr auto size = 20ul;
    int a[size];
    std::memset(a, size, 0);
}
prog.cc: In function 'int main()':
prog.cc:7:27: warning: 'memset' used with constant zero length parameter; this could be due to transposed parameters [-Wmemset-transposed-args]
    7 |     std::memset(a, size, 0);
      |                           ^
, . if, else, while for. :
int main ()
{
    int x;
    if (true)
        x = 3;
        return x;
}
prog.cc: In function 'int main()':
prog.cc:4:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
    4 |     if (true)
      |     ^~
prog.cc:6:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
    6 |         return x;
      |         ^~~~~~
. -Wempty-body.
, , .
template <class T>
T* __attribute__ ((malloc, alloc_size (1)))
foo (long);
template <>
void* __attribute__ ((malloc))
foo<void> (long);
int main ()
{
}
prog.cc:7:1: warning: explicit specialization 'T* foo(long int) [with T = void]' may be missing attributes [-Wmissing-attributes]
    7 | foo<void> (long);
      | ^~~~~~~~~
prog.cc:3:1: note: missing primary template attribute 'alloc_size'
    3 | foo (long);
      | ^~~
, , if, else, while for. , , , :
#define INCREMENT x++; y++
int main ()
{
    int x = 0;
    int y = 0;
    if (true)
        INCREMENT;
}
prog.cc: In function 'int main()':
prog.cc:1:19: warning: macro expands to multiple statements [-Wmultistatement-macros]
    1 | #define INCREMENT x++; y++
      |                   ^
prog.cc:8:9: note: in expansion of macro 'INCREMENT'
    8 |         INCREMENT;
      |         ^~~~~~~~~
prog.cc:7:5: note: some parts of macro expansion are not guarded by this 'if' clause
    7 |     if (true)
      |     ^~
. -Wmisleading-indentation.
, nonnull.
void f (int * ptr) __attribute__((nonnull));
int main ()
{
    f(nullptr);
}
void f (int *) {}
prog.cc: In function 'int main()':
prog.cc:5:14: warning: null argument where non-null required (argument 1) [-Wnonnull]
    5 |     f(nullptr);
      |              ^
, nonnull.
bool f (int * ptr) __attribute__((nonnull));
int main ()
{
    f(nullptr);
}
bool f (int * p)
{
    return p == nullptr;
}
prog.cc: In function 'bool f(int*)':
prog.cc:10:17: warning: nonnull argument 'p' compared to NULL [-Wnonnull-compare]
   10 |     return p == nullptr;
      |                 ^~~~~~~
— , :
int main ()
{
    int x = 5;
    if (x = 4)
    {
        x = 3;
    }
}
, , :
prog.cc: In function 'int main()':
prog.cc:4:11: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    4 |     if (x = 4)
      |         ~~^~~
, , :
int main ()
{
    int x = 5;
    if ((x = 4))
    {
        x = 3;
    }
}
. :
#include <utility>
struct A {};
A f ()
{
    A a;
    return std::move(a);
}
int main ()
{
    f();
}
prog.cc: In function 'A f()':
prog.cc:8:21: warning: moving a local object in a return statement prevents copy elision [-Wpessimizing-move]
    8 |     return std::move(a);
      |            ~~~~~~~~~^~~
prog.cc:8:21: note: remove 'std::move' call
, . , .
struct A
{
    int i;
    int j;
    A(): j(0), i(1)
    {
    }
};
int main () {}
prog.cc: In constructor 'A::A()':
prog.cc:4:9: warning: 'A::j' will be initialized after [-Wreorder]
    4 |     int j;
      |         ^
prog.cc:3:9: warning:   'int A::i' [-Wreorder]
    3 |     int i;
      |         ^
prog.cc:6:5: warning:   when initialized here [-Wreorder]
    6 |     A(): j(0), i(1)
      |     ^
, :
int f ()
{
}
int main () {}
prog.cc: In function 'int f()':
prog.cc:3:1: warning: no return statement in function returning non-void [-Wreturn-type]
    3 | }
      | ^
. ( ):
int main ()
{
    int a = 6;
    ++a = a++;
}
prog.cc: In function 'int main()':
prog.cc:4:12: warning: operation on 'a' may be undefined [-Wsequence-point]
    4 |     ++a = a++;
      |           ~^~
. , - . , :
int main ()
{
    short a = -5;
    unsigned b = 4;
    if (a < b)
    {
        return 0;
    }
    return 1;
}
prog.cc: In function 'int main()':
prog.cc:5:11: warning: comparison of integer expressions of different signedness: 'short int' and 'unsigned int' [-Wsign-compare]
    5 |     if (a < b)
      |         ~~^~~
sizeof, . , , :
int main ()
{
    const char * a = "12345";
    auto size = sizeof (a) / sizeof (a[0]);
}
prog.cc: In function 'int main()':
prog.cc:4:28: warning: division 'sizeof (const char*) / sizeof (const char)' does not compute the number of array elements [-Wsizeof-pointer-div]
    4 |     auto size = sizeof (a) / sizeof (a[0]);
      |                 ~~~~~~~~~~~^~~~~~~~~~~~~~~
prog.cc:3:18: note: first 'sizeof' operand was declared here
    3 |     const char * a = "12345";
      |                  ^
, (str..., mem... ..), sizeof. :
#include <cstring>
int main ()
{
    char a[10];
    const char * s = "qwerty";
    std::memcpy (a, s, sizeof(s));
}
prog.cc: In function 'int main()':
prog.cc:7:24: warning: argument to 'sizeof' in 'void* memcpy(void*, const void*, size_t)' call is the same expression as the source; did you mean to provide an explicit length? [-Wsizeof-pointer-memaccess]
    7 |     std::memcpy (a, s, sizeof(s));
      |                        ^~~~~~~~~
(strict aliasing) — . .
, .
, switch:
enum struct enum_t
{
    a, b, c
};
int main ()
{
    enum_t e = enum_t::a;
    switch (e)
    {
        case enum_t::a:
        case enum_t::b:
            return 0;
    }
}
prog.cc: In function 'int main()':
prog.cc:9:12: warning: enumeration value 'c' not handled in switch [-Wswitch]
    9 |     switch (e)
      |            ^
:
int main ()
{
    int i = 1;
    if (i > i);
}
prog.cc: In function 'int main()':
prog.cc:4:11: warning: self-comparison always evaluates to false [-Wtautological-compare]
    4 |     if (i > i);
      |         ~ ^ ~
, , ( ):
int main ()
{
    int i = 1;
    if ((i & 16) == 10);
}
prog.cc: In function 'int main()':
prog.cc:4:18: warning: bitwise comparison always evaluates to false [-Wtautological-compare]
    4 |     if ((i & 16) == 10);
      |         ~~~~~~~~ ^~ ~~
, . , , .
. -Wcomment.
, :
int main ()
{
    int x;
    return x;
}
prog.cc: In function 'int main()':
prog.cc:4:12: warning: 'x' is used uninitialized in this function [-Wuninitialized]
    4 |     return x;
      |            ^
, , , , , inline, .
, .
int main ()
{
    int x = 0;
}
prog.cc: In function 'int main()':
prog.cc:3:9: warning: unused variable 'x' [-Wunused-variable]
    3 |     int x = 0;
      |         ^
, , , static_cast<void>(...):
int main ()
{
    int x = 0;
    static_cast<void>(x);
}
"" . , -Wall ( -Wall, ).
do-while. , :
int main ()
{
    if (true);
    {
        return 1;
    }
}
prog.cc: In function 'int main()':
prog.cc:3:14: warning: suggest braces around empty body in an 'if' statement [-Wempty-body]
    3 |     if (true);
      |              ^
. -Wmisleading-indentation.
"" switch:
int main ()
{
    int x = 7;
    int a;
    switch (x)
    {
        case 1:
            a = 1;
            break;
        case 2:
            a = 2;
        case 3:
            a = 3;
            break;
        default:
            a = 0;
    }
    return a;
}
, break, case 2 :
prog.cc: In function 'int main()':
prog.cc:11:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
   11 |             a = 2;
      |             ~~^~~
prog.cc:13:9: note: here
   13 |         case 3:
      |         ^~~~
C++17 — fallthrough:
int main ()
{
    int x = 7;
    int a;
    switch (x)
    {
        case 1:
            a = 1;
            break;
        case 2:
            a = 2;
            [[fallthrough]];
        case 3:
            a = 3;
            break;
        default:
            a = 0;
    }
    return a;
}
, . :
struct S
{
    int f;
    int g;
    int h;
};
int main ()
{
    S s{3, 4};
}
prog.cc: In function 'int main()':
prog.cc:10:13: warning: missing initializer for member 'S::h' [-Wmissing-field-initializers]
   10 |     S s{3, 4};
      |             ^
std::move , , :
#include <utility>
struct S {};
S f (S s)
{
    return std::move(s);
}
int main ()
{
    auto s = f(S{});
}
prog.cc: In function 'S f(S)':
prog.cc:7:21: warning: redundant move in return statement [-Wredundant-move]
    7 |     return std::move(s);
      |            ~~~~~~~~~^~~
prog.cc:7:21: note: remove 'std::move' call
, . , . , , , , , , . , - :
int main ()
{
    unsigned x = 17;
    if (x >= 0)
    {
        return 1;
    }
}
prog.cc: In function 'int main()':
prog.cc:4:11: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
    4 |     if (x >= 0)
      |         ~~^~~~
. , :
int main ()
{
    const int x = -7;
    return x << 4;
}
prog.cc: In function 'int main()':
prog.cc:4:17: warning: left shift of negative value [-Wshift-negative-value]
    4 |     return x << 4;
      |                 ^
. , , .
void f (int x) {}
int main ()
{
}
prog.cc: In function 'void f(int)':
prog.cc:1:13: warning: unused parameter 'x' [-Wunused-parameter]
    1 | void f (int x) {}
      |         ~~~~^
C++17 maybe_unused:
void f ([[maybe_unused]] int x) {}
int main ()
{
}
, , . , :
void f (int x)
{
    x = 7;
}
int main ()
{
}
prog.cc: In function 'void f(int)':
prog.cc:1:13: warning: parameter 'x' set but not used [-Wunused-but-set-parameter]
    1 | void f (int x)
      |         ~~~~^
-Wall -Wextra — , .
-Wpedantic ( -pedantic), ISO C++, , , .
. , - "", .
, , , - .
class base
{
    base () {};
    ~base() {};
};
int main ()
{
}
prog.cc:1:7: warning: 'class base' only defines a private destructor and has no friends [-Wctor-dtor-privacy]
    1 | class base
      |       ^~~~
, , -, .
, -, . . , .
struct base
{
    virtual void f (int) {}
    ~base() {};
};
int main ()
{
}
prog.cc:1:8: warning: 'struct base' has virtual functions and accessible non-virtual destructor [-Wnon-virtual-dtor]
    1 | struct base
      |        ^~~~
C. static_cast, dynamic_cast, reinterpret_cast const_cast, . — , , — . .
- :
struct base
{
    virtual void f (int) {}
};
struct derived: base
{
    void f () {};
};
int main ()
{
}
prog.cc:3:18: warning: 'virtual void base::f(int)' was hidden [-Woverloaded-virtual]
    3 |     virtual void f (int) {}
      |                  ^
prog.cc:8:10: warning:   by 'void derived::f()' [-Woverloaded-virtual]
    8 |     void f () {};
      |          ^
. :
void f (int) {}
void f (unsigned) {}
int main ()
{
    unsigned short x = 7;
    f(x);
}
prog.cc: In function 'int main()':
prog.cc:7:8: warning: passing 'short unsigned int' chooses 'int' over 'unsigned int' [-Wsign-promo]
    7 |     f(x);
      |        ^
prog.cc:7:8: warning:   in call to 'void f(int)' [-Wsign-promo]
, - , . - , .
, if else :
int main ()
{
    if (true)
        return 0;
    else
        return 0;
}
prog.cc: In function 'int main()':
prog.cc:3:5: warning: this condition has identical branches [-Wduplicated-branches]
    3 |     if (true)
      |     ^~
?: :
int main ()
{
    auto x = true ? 4 : 4;
}
prog.cc: In function 'int main()':
prog.cc:3:19: warning: this condition has identical branches [-Wduplicated-branches]
    3 |     auto x = true ? 4 : 4;
      |              ~~~~~^~~~~~~
, , -Wall, .
. -Wduplicated-cond.
if-else-if:
int main ()
{
    auto x = 6;
    if (x > 7) {return 0;}
    else if (x > 7) {return 1;}
}
prog.cc: In function 'int main()':
prog.cc:5:10: warning: duplicated 'if' condition [-Wduplicated-cond]
    5 |     else if (x > 7) {return 1;}
      |          ^~
prog.cc:4:5: note: previously used here
    4 |     if (x > 7) {return 0;}
      |     ^~
. -Wduplicated-branches.
. , , .
( , ), std::equal_to, .
, , .
, . , const.
, , . ( ) , . :
int main ()
{
    double x = 4.5;
    int y = x;
}
prog.cc: In function 'int main()':
prog.cc:12:13: warning: conversion from 'double' to 'int' may change value [-Wfloat-conversion]
   12 |     int y = x;
      |             ^
, .
nullptr.
. -.
-Wconversion , :
int main ()
{
    signed si = -8;
    unsigned ui;
    ui = si;
}
prog.cc: In function 'int main()':
prog.cc:5:10: warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
    5 |     ui = si;
      |          ^~
. , "" "", :
int main ()
{
    int a = 8;
    if (a < 0 && a < 0)
    {
        return 1;
    }
}
prog.cc: In function 'int main()':
prog.cc:4:15: warning: logical 'and' of equal expressions [-Wlogical-op]
     if (a < 0 && a < 0)
         ~~~~~~^~~~~~~~
, , . . .
. , — - , , .
-Werror -pedantic-errors, -Wpedantic -Werror.
, .
, GCC (Clang - , ) , .
-Werror
-pedantic-errors
-Wall
-Wextra
-Wpedantic
-Wcast-align
-Wcast-qual
-Wconversion
-Wctor-dtor-privacy
-Wduplicated-branches
-Wduplicated-cond
-Wextra-semi
-Wfloat-equal
-Wlogical-op
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wredundant-decls
-Wsign-conversion
-Wsign-promo
是的,这样的标志列表会产生大量错误,乍一看似乎没有必要。但是显式胜于隐式。如果您知道自己在做什么,那就去做吧。但是这样做是为了让每个人都明白这正是您想要的。以这种方式工作了至少一周,您将意识到它的美妙之处,并且您将无法返回。
爱您的编译器,并帮助他帮助您编写程序。
- GCC编译器文档
- Clang编译器文档