Ayuda al compilador a ayudarte

Prefacio


Los compiladores modernos tienen una gran cantidad de diagnósticos. Y sorprendentemente, muy pocos de ellos están habilitados por defecto.


Una gran cantidad de reclamos que se presentan al lenguaje C ++ en estos sitios de Internet (sobre complejidad, inseguridad, disparos en las piernas, etc.) se refieren precisamente a aquellos casos en que las personas simplemente no saben que estos problemas pueden resolverse Deslice el dedo sobre el teclado.


Arreglemos esta injusticia flagrante y arrojemos la luz de la verdad sobre la capacidad del compilador para evitar errores.



Contenido


  1. Oda al compilador
  2. Ignorar no se puede arreglar
  3. -Pared
  4. -Wextra
  5. -Muy maravilloso
  6. Necesita más advertencias
  7. -Terror
  8. ConclusiĂłn
  9. Referencias


Oda al compilador


El compilador es el mejor amigo del plus. Un compilador no es solo un traductor de un lenguaje formal legible por humanos en códigos de máquina. El compilador es el mejor ayudante para escribir programas.


Una ayuda importante (y no la única) proporcionada por el compilador es la búsqueda de errores. Y no estoy hablando de errores tipográficos, falta de coincidencia de tipos y otros errores de sintaxis. Estoy hablando de un gran conjunto de errores que pueden detectarse utilizando el mecanismo de advertencia.


, , , , , "" .. , .




— "" , . — . — , , , , - .


, . , , , . , , . static_cast , , .


, .


, .


, C++ GCC (, Clang). .


-Wall


-Wall — "" . C++ , ( , , , ).


:

, , .



-Waddress


. , . , :


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")
      |         ~~^~~~~~~~


-Warray-bounds=1


. -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};
      |         ^


-Wbool-compare


, :


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)
      |         ~~~~~~~~^~~~


-Wbool-operation


. , :


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 , .



-Wcatch-value


, :


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 (. ).



-Wchar-subscripts


, 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];
      |              ^~~~~


-Wcomment


, (/*), , .


int main ()
{
    /* asd /* fgh */
    //qwe\
    rty
}

prog.cc:3:8: warning: "/*" within comment [-Wcomment]
    3 |     /* /* */
      |
prog.cc:4:5: warning: multi-line comment [-Wcomment]
    4 |     //ssd\
      |     ^


-Wint-in-bool-context


, , , :


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);
      |         ~~^~~


-Winit-self


. -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;
      |         ^


-Wlogical-not-parentheses


. , .


, :


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);
      |         ^~
      |         ( )

, — , .



-Wmaybe-uninitialized


, .


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;
}


-Wmemset-elt-size


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);
      |                           ^


-Wmemset-transposed-args


, , , 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);
      |                           ^


-Wmisleading-indentation


, . 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.



-Wmissing-attributes


, , .


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);
      | ^~~


-Wmultistatement-macros


, , 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.



-Wnonnull


, 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);
      |              ^


-Wnonnull-compare


, 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;
      |                 ^~~~~~~


-Wparentheses


— , :


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;
    }
}


-Wpessimizing-move


. :


#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


-Wreorder


, . , .


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)
      |     ^


-Wreturn-type


, :


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 | }
      | ^


-Wsequence-point


. ( ):


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++;
      |           ~^~


-Wsign-compare


. , - . , :


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)
      |         ~~^~~


-Wsizeof-pointer-div


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";
      |                  ^


-Wsizeof-pointer-memaccess


, (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));
      |                        ^~~~~~~~~


-Wstrict-aliasing


(strict aliasing) — . .


, .



-Wswitch


, 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)
      |            ^


-Wtautological-compare


:


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);
      |         ~~~~~~~~ ^~ ~~


-Wtrigraphs


, . , , .


. -Wcomment.



-Wuninitialized


, :


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;
      |            ^


-Wunused-function


, , , , , inline, .



-Wunused-variable


, .


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);
}


-Wextra


"" . , -Wall ( -Wall, ).




-Wempty-body


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.



-Wimplicit-fallthrough


"" 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;
}


-Wmissing-field-initializers


, . :


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};
      |             ^


-Wredundant-move


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


-Wtype-limits


, . , . , , , , , , . , - :


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)
      |         ~~^~~~


-Wshift-negative-value


. , :


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;
      |                 ^


-Wunused-parameter


. , , .


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 ()
{
}


-Wunused-but-set-parameter


, , . , :


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)
      |         ~~~~^


-Wpedantic


-Wall -Wextra — , .


-Wpedantic ( -pedantic), ISO C++, , , .




. , - "", .




-Wctor-dtor-privacy


, , , - .


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
      |       ^~~~

, , -, .



-Wnon-virtual-dtor


, -, . . , .


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
      |        ^~~~


-Wold-style-cast


C. static_cast, dynamic_cast, reinterpret_cast const_cast, . — , , — . .



-Woverloaded-virtual


- :


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 () {};
      |          ^


-Wsign-promo


. :


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]

, - , . - , .



-Wduplicated-branches


, 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.



-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.



-Wfloat-equal


. , , .


( , ), std::equal_to, .



-Wshadow=compatible-local


, , .



-Wcast-qual


, . , const.



-Wconversion


, , . ( ) , . :


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;
      |             ^

, .



-Wzero-as-null-pointer-constant


nullptr.



-Wextra-semi


. -.



-Wsign-conversion


-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;
      |          ^~


-Wlogical-op


. , "" "", :


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


, , . . .


. , — - , , .


-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

Sí, dicha lista de indicadores puede generar una gran cantidad de errores, que al principio pueden parecer innecesarios. Pero explícito es mejor que implícito. Si sabes lo que estás haciendo, hazlo. Pero hazlo para que todos entiendan que eso es exactamente lo que querías. Después de haber trabajado de esta manera durante al menos una semana, se dará cuenta de lo maravilloso que es y no podrá regresar.


Ama a tu compilador y ayĂşdalo a ayudarte a escribir programas.



Referencias


  1. DocumentaciĂłn del compilador GCC
  2. DocumentaciĂłn del compilador de Clang

All Articles