2010-01-20 13 views
42

Szukam łatwego sposobu na znalezienie niezainicjowanych zmiennych członkowskich klasy.Łatwy sposób znaleźć niezainicjowane zmienne składowe

Znaleźć je w runtime lub czas kompilacji jest OK.

Obecnie mam punkt przerwania w konstruktorze klasy i sprawdzam zmienne składowe jeden po drugim.

+0

Dobry artykuł "W poszukiwaniu niezainicjowanych członków klasy" - http://www.viva64.com/en/b/0354/ –

+0

W przypadku korzystania z kompilatora clang można wypróbować dezynfekcję pamięci: http: //clang.llvm .org/docs/MemorySanitizer.html. Wykonuje dynamiczne sprawdzanie i ma znacznie mniejszy narzut w porównaniu do valgrind. Jest ładna prezentacja od autora na cppcon2014 https://www.youtube.com/watch?v=V2_80g0eOMc – Jurasic

Odpowiedz

6

-Wuninitialized?

(To tylko sprawdza, czy zmienna jest używana niezainicjowany, czyli jeśli

struct Q { 
    int x, y; 
    Q() : x(2) {} 
    int get_xy() const { return x*y; } 
}; 

g ++ ostrzeże tylko wtedy, gdy użytkownik wywołuje get_xy() bez przypisywania do y.)

+1

Wymaga również -O1 lub nowszej wersji, która nie jest domyślna. –

+0

Nie mogę uzyskać g ++ 4.3.3, aby ostrzec członków danych o - Wuninitialized, jesteś pewien, że to działa tutaj? (Prosty test: dodaj 'int main() {return Q(). Get_xy();}' do twojego kodu.) –

+0

@ Roger-plate: Niestety musisz użyć 'int main() {Q q; return q.get_xy(); } 'do pracy. – kennytm

10

Valgrind (darmo, Linux) i Purify (w systemie Windows) znaleźć un-zainicjowany zmienne, nieprawidłowe wskaźniki i takie, uruchamiając swój kod w specjalnej maszynie wirtualnej.

Jest łatwy w użyciu i niezwykle wydajny; prawdopodobnie wykryje wiele błędów poza oczywistymi nie zainicjowanymi zmiennymi.

Coverity, Klocwork i Lint może znaleźć niezainicjalizowane zmienne za pomocą statycznej analizy kodu.

+0

* "Łatwy w użyciu" * jest subiektywny. Z rozszerzonym blokiem montażowym GCC wszystko, co dostajemy, to numer linii wskazujący koniec bloku (okienko zamykające), a nie rzeczywista zmienna powodująca problem. To nawet z '--track-origins'. – jww

+0

@jww to jest problem z gcc (brak wystarczającej ilości informacji na temat debugowania), a nie problem z valgrindem. Może zniknąć, jeśli skompilujesz swoje źródło przy pomocy -ggdb lub czegoś podobnego, ale byłbym zaskoczony. Pracuję w backendach LIVM i tam, w podobnej sytuacji. – Will

+0

Wygląda na to, że oczyszczanie nie jest już dobrym rozwiązaniem: http://marlowa.blogspot.com.br/2015/08/the-death-of-purify.html – VinGarcia

4

Jeśli używasz programu Visual Studio, możesz skompilować go w trybie debugowania, zatrzymać program w debugerze i sprawdzić, które zmienne zostały zainicjowane na bajtach zawierających 0xCC (stos) lub 0xCD (stertę).

Chociaż osobiście zainwestowałbym w narzędzie do analizy statycznej, aby uzyskać dokładniejsze podejście.

2

/analyze on Visual Studio („System Team”)

+3

Czy na pewno działa to dla niezainicjowanych zmiennych * member *? W naszych testach znajduje tylko niezainicjowane zmienne lokalne. – Agentlien

23

Jeśli używasz GCC można użyć flagi -Weffc++, który generuje ostrzeżenia, gdy zmienna nie jest inicjowany na liście członek inicjalizacji. To:

class Foo 
{ 
    int v; 
    Foo() {} 
}; 

prowadzi do:

$ g++ -c -Weffc++ foo.cpp -o foo.o 
foo.cpp: In constructor ‘Foo::Foo()’: 
foo.cpp:4: warning: ‘Foo::v’ should be initialized in the member initialization list 

Jednym minusem jest to, że -Weffc++ będzie również ostrzegać, gdy zmienna ma odpowiedniego inicjalizacji domyślnego konstruktora i tym samym nie byłoby konieczne. Ostrzeże Cię również, gdy zainicjujesz zmienną w konstruktorze, ale nie na liście inicjalizacji członków. I ostrzega o wielu innych problemach w stylu C++, takich jak brak konstruktorów kopiowania, więc może być konieczne oczyszczenie kodu nieco, gdy chcesz regularnie używać -Weffc++.

Istnieje również błąd, który powoduje, że zawsze daje ostrzeżenie przy użyciu anonimowych związki, które obecnie nie mogą obejść inne następnie przejście ostrzeżenie, co można zrobić z off:

#pragma GCC diagnostic ignored "-Weffc++" 

Ogólnie jednak znalazłem -Weffc++, aby był niesamowicie przydatny w łapaniu wielu typowych błędów w C++.

+0

to nadal błąd? –

8

cppcheck znajdzie to, np:

cppcheck my_src_dir --output-file=check.txt --inconclusive --enable=warning 
+0

Co się stało ze zdaniem? –

+1

Prawdopodobnie dlatego, że cppcheck nie jest tak inteligentny. Ostrzeże przed nieinicjalizowaniem w konstruktorze, ale najczęściej nie może badać skomplikowanych ścieżek, gdzie np. Ustawiające w konstruktorach inicjują element. –

+1

Cppcheck został poprawiony od tego czasu, wersja 1.75 jest w stanie wykryć tylko częściowe inicjalizacje struct. Ale oczywiście niektóre przypadki, takie jak [this one] (http://stackoverflow.com/q/38829964/2932052) są nadal zbyt trudne dla niego, choć również trudne dla ludzi (jak zauważyłem). – Wolf

-2

Rozważmy następujący kod

unint.cpp:

int main() 
{ 
    int a; 
    int b; 
    a++; 
    b = b + 5; 

    return 0; 
} 

Jeśli kod zostanie skompilowany z następującym komentarzem, komunikaty ostrzegawcze być wyświetlane.

g ++ -O3 -Wuninitialized unint.cpp

Uwaga: -Wuninitialized potrzebuje również opcja -O3.

+0

Dane wyjściowe: unint.cpp: W funkcji 'int main()': unint.cpp: 8: warning: 'a' jest używane niezainicjalizowane w tej funkcji unint.cpp: 9: warning: 'b' jest używane niezainicjalizowane w tej funkcji – Tamilalagan

+0

Pytanie jest zatytułowane "Łatwa droga [do] znajdowania niezainicjowanych ** elementów ** zmiennych". Nie są to zmienne składowe. Wszyscy wiemy o '-Wunitialized' dla zmiennych nie będących członkami, i wszyscy powinniśmy to mieć już jako część już używanego' -Wall -Wextra -Wpedantic'. Ponadto nie "potrzebuje opcji -O3" ani żadnej innej formy optymalizacji, która może być obecna lub nieobecna, chociaż mogą wpłynąć na ostrzeżenia, które zostaną zwrócone, jeśli optymalizacja spowoduje usunięcie zmiennych o skutkach ubocznych. –

0

Uwaga! Proponowane tutaj opcje kompilatora nie są ani niezawodne, ani niezależne od wersji. Rozważmy prosty przykład:

class A { 
    int a; 
public: 
    void mA() { 
    printf("haha"); 
    ++a; 
    int g = 2/a; 
    printf("%i\n",g); 
    } 
}; 

int main() { 
    A a; 
    a.mA(); 
} 

Zestawione z g++ -O3 -Weffc++ -Wuninitialized ta rzecz donosi uninitialized w wersjach gcc do 4,6 włącznie, a passess szczęśliwie na 4,7 i 4,8 (testowane na DarwinPorts). Następnie, co ciekawe, jeśli usuniemy printf("haha");, zarówno 4.7 jak i 4.8 nagle zobaczą uninitialized A::a. Clang jest trochę lepszy, ponieważ w jakiś sposób przypisuje śmieci (zamiast wygodnych 0) niezainicjowanym vars, więc widzisz ich katastrofalny efekt łatwiej/wcześniej.

Nie miałem szczęścia w wykryciu powyższego niezainicjowanego A::a z valgrind; może gentlement sugerujący, że może zapewnić odpowiednie opcje, aby wykryć ten błąd.

Konkluzja: świetne pytanie, niewiele niezawodnych rozwiązań w tej chwili ... (tak jak to widzę).

+2

Przy dowolnym poziomie optymalizacji powyżej '-O0', gcc 4.7 optymalizuje wszystko oprócz wywołań' printf'. Sprawdziłem kod zestawu i to tylko dwa połączenia z 'printf'. Zatem ocena w czasie kompilacji gcc nie wykrywa niezainicjowanej wartości. A "valgrind" nie ma szansy na wykrycie go w czasie wykonywania, ponieważ jest to tylko dwa wywołania printf ze stałymi argumentami. –

5

Visual Studio (MSVC) ma opcję kompilatora/sdl (Enable Additional Security Checks) (http://msdn.microsoft.com/en-us/library/jj161081.aspx). W czasie wykonywania:

Dokonuje inicjalizacji klasy klasy. Automatycznie inicjuje klasę elementów typu wskaźnika na zero podczas tworzenia obiektów (przed uruchomieniem konstruktora ). Pomaga to zapobiegać używaniu niezainicjowanych danych związanych z elementami klasy, które konstruktor nie inicjuje jawnie .

To nie pomoże ci wykryć niezainicjowanych zmiennych członkowskich podczas kompilacji, ale sprawi, że zachowanie stanie się bardziej przewidywalne, gdy zdarzy się w czasie wykonywania. Nie powinieneś oczywiście pisać kodu, który polega na włączeniu tej opcji.

0

Clang z clang-analyze jest w stanie to zrobić. To wydarzenie utworzy ładny raport HTML, wskazujący, kiedy dostęp do niewykorzystanej zmiennej jest możliwy.

Powiązane problemy