2010-01-20 2 views
42

Я ищу простой способ найти неинициализированные переменные-члены класса.Простой способ найти неинициализированные переменные-члены

Обнаружение их в время выполнения или время компиляции в порядке.

В настоящее время у меня есть точка останова в конструкторе класса и поочередно проверяю переменные-члены.

+0

Хорошая статья «В поисках неинициализированных членов класса» - http://www.viva64.com/ru/b/0354/ – 2015-10-29 14:38:18

+0

В случае использования компилятора clang вы можете попробовать дезинфицирующее средство для хранения данных: http: //clang.llvm .org/документы/MemorySanitizer.html. Он выполняет динамическую проверку и имеет значительно меньшие накладные расходы по сравнению с valgrind. Существует хорошая презентация от автора на cppcon2014 https://www.youtube.com/watch?v=V2_80g0eOMc – Jurasic

ответ

6

-Wuninitialized?

(Это только проверяет, является ли переменная используется неинициализированным, т.е. если

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

г ++ предупредит только тогда, когда пользователь вызывает get_xy() без присвоения y.)

+1

Также требуется -O1 или выше, что не является значением по умолчанию. – 2010-01-20 07:59:10

+0

Я не могу получить g ++ 4.3.3, чтобы предупредить членов данных с -Wuninitialized, вы уверены, что он работает здесь? (Простой тест: добавьте 'int main() {return Q(). Get_xy();}' к вашему коду.) – 2010-01-20 08:02:55

+0

@ Roger-plate: К сожалению, вам нужно использовать 'int main() {Q q; return q.get_xy(); } 'для работы. – kennytm

10

Valgrind (FREE, на Linux) и Purify (на Windows) найти не-инициализируются переменные, недопустимые указатели и такие, запустив код в специальной виртуальной машине.

Это прост в использовании и чрезвычайно мощный; он, скорее всего, найдет много ошибок за пределами очевидных неинициализированных переменных.

Coverity, Klocwork и Lint могут найти неинициализированные переменные, используя статический анализ кода.

+0

* «Прост в использовании» * субъективен. С расширенным сборным блоком GCC все, что мы получаем, это номер строки, указывающий на конец блока (заключительный параграф), а не фактическую переменную, вызывающую проблему. Это даже с '--track-originins'. – jww

+0

@jww Это проблема gcc (не создавая достаточно информации об отладке), а не проблема valgrind. Это может исчезнуть, если вы скомпилируете свой источник с помощью -ggdb или что-то еще, но я был бы удивлен. Я работаю в llvm-бэкэндах и там очень похожая ситуация. – Will

+0

Кажется, что очистка больше не является хорошим вариантом: http://marlowa.blogspot.com.br/2015/08/the-death-of-purify.html – VinGarcia

4

Если вы используете Visual Studio, вы можете скомпилировать его в режиме отладки, остановить программу в отладчике и посмотреть, какие переменные инициализируются байтами, содержащими 0xCC (стек) или 0xCD (куча).

Хотя лично я инвестировал в инструмент статического анализа для более тщательного подхода.

2

/analyze on Visual Studio («Team System»)

+3

Вы уверены, что это работает для неинициализированных переменных * члена *? В наших тестах он находит только неинициализированные локальные переменные. – Agentlien

23

Если вы используете GCC вы можете использовать -Weffc++ флаг, который генерирует предупреждения, когда переменная не инициализируется в списке элемент инициализации. Это:

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

ведет к:

$ 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 

Одним из недостатков является то, что -Weffc++ также предупредит вас, когда переменная имеет правильный конструктор по умолчанию и инициализацию таким образом, не будет необходимости. Он также предупредит вас, когда вы инициализируете переменную в конструкторе, но не в списке инициализации члена. И он предупреждает о многих других проблемах стиля C++, таких как отсутствующие конструкторы-копии, поэтому вам может понадобиться немного очистить свой код, если вы хотите регулярно использовать -Weffc++.

Существует также ошибка, которая заставляет его всегда дать вам предупреждение при использовании анонимных союзов, которые вы в настоящее время не могут работать вокруг другого, то выключая предупреждение, которое может быть сделано с:

#pragma GCC diagnostic ignored "-Weffc++" 

В целом, однако я нашел -Weffc++, чтобы быть невероятно полезным в ловле множества распространенных ошибок C++.

+0

ошибка там еще? –

8

cppcheck найти это, например:

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

Что с downvote? –

+1

Наверное, потому что cppcheck не такой умный. Он будет предупреждать об отсутствии инициализации в конструкторе, но чаще всего не может исследовать сложные пути, где, например, сеттеры в конструкторах инициализируют член. –

+1

Cppcheck был улучшен с тех пор, версия 1.75 способна обнаруживать только частичные инициализации структуры. Но, конечно, некоторые случаи, такие как [этот] (http://stackoverflow.com/q/38829964/2932052), по-прежнему слишком сложны для него, хотя также и для людей (как я заметил). – Wolf

-2

Рассмотрим следующий код

unint.cpp:

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

    return 0; 
} 

Если код скомпилирован с следующим комментарием, предупреждающие сообщения должны будет отображаться.

г ++ -O3 -Wuninitialized unint.cpp

Примечание: -Wuninitialized необходим параметр -O3 также.

+0

Выход: unint.cpp: В функции 'int main()': unint.cpp: 8: warning: 'a' используется неинициализированным в этой функции unint.cpp: 9: warning: 'b' используется неинициализированный в этой функции – Tamilalagan

+0

Вопрос под названием «Простой способ [найти] неинициализированные ** член ** переменные». Они не являются переменными-членами. Мы все знаем о '-Wunitialized' для переменных, не являющихся членами, и все мы должны иметь это уже как часть уже использующего' -Wall -Wextra -Wpedantic'. Кроме того, он не нуждается в опции -O3 или какой-либо другой форме оптимизации, чтобы присутствовать или отсутствовать, хотя они могут влиять на то, какие предупреждения возвращаются, если оптимизация приводит к удалению побочных эффектов. –

0

Опасайтесь! Варианты компилятора, предложенные здесь, не являются ни надежными, ни независимыми от версии. Рассмотрим простой пример:

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

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

Собран с g++ -O3 -Weffc++ -Wuninitialized эта вещь сообщает uninitialized на GCC версии до 4.6 включительно, и passess счастливо на 4.7 и 4.8 (проверено на MacPorts). Затем, с любопытством, если мы удалим printf("haha");, то как 4.7, так и 4.8 вдруг увидим uninitialized A::a. Clang немного лучше, так как он как-то присваивает мусор (вместо удобного 0) неинициализированным vars, так что вы видите их катастрофический эффект легче/раньше.

Мне не повезло, если вы заметили выше неинициализированный A::a с valgrind; возможно, предложение, предлагающее valgrind, может предоставить соответствующие варианты для обнаружения этой ошибки.

Итог: большой вопрос, не очень надежные решения на данный момент ... (как я его вижу).

+2

При любом уровне оптимизации выше '-O0', gcc 4.7 оптимизирует все, кроме вызовов' printf'. Я проверил код сборки, и это всего два вызова 'printf'. Таким образом, оценка времени компиляции gcc не определяет неинициализированное значение. И 'valgrind' не имеет возможности обнаружить его во время выполнения, так как это только два вызова printf с постоянными аргументами. –

5

Visual Studio (MSVC) имеет опцию/sdl (Включить дополнительные проверки безопасности) (http://msdn.microsoft.com/en-us/library/jj161081.aspx). Во время выполнения это:

Выполняет инициализацию члена класса. Автоматически инициализирует класс членов типа указателя до нуля при создании объекта (до запуска конструктора ). Это помогает предотвратить использование неинициализированных данных , связанных с членами класса, что конструктор явно не объявляет .

Это не поможет вам определить неинициализированные переменные-члены во время компиляции, но делает поведение более предсказуемым, когда это происходит во время выполнения. Разумеется, вы не должны писать код, который полагается на этот параметр.

0

Clang with clang-analysis способен это сделать. Это событие создаст хороший HTML-отчет, который указывает, когда доступ к неиспользуемой переменной.