2016-08-10 3 views
3

Valgrind полезен для обнаружения затяжных ссылок на свободные объекты в куче. Тем не менее, похоже, что эта функция не содержит затянувшихся ссылок на переменные вне области видимости в стеке. Например:Как определить ссылки на переменные стека вне области видимости в C++?

#include <iostream> 

struct CharHolder { 
    const char ch; 
    CharHolder(char _ch) : ch(_ch) {} 
}; 

struct Printer { 
    const CharHolder& ref; 
    Printer(const CharHolder& _ref) : ref(_ref) {} 
    void print() { 
     std::cout << &ref << ": " << ref.ch << std::endl; 
    } 
}; 

int main() { 
    // g++ -O0: prints 'x' 
    // g++ -O3: prints undefined character 
    Printer p1(CharHolder('x')); 
    p1.print(); 

    // g++: prints undefined character 
    CharHolder* h = new CharHolder('x'); 
    Printer p2(*h); 
    delete h; 
    p2.print(); 
} 

В первом примере, с p1, является тот, где принтер содержит ссылку на переменный стек вне области видимости, так как CharHolder('x') разрушаются, как только строительство p1 завершено.

Второй пример, с p2, это когда принтер содержит ссылку на переменную кучи, которая free'd перед тем p2 попытки ссылаться на него в print().

Valgrind жалуется на втором примере:

==82331== Invalid read of size 1 
==82331== at 0x400A8E: Printer::print() 
==82331== by 0x400967: main 
==82331== Address 0x5a1c040 is 0 bytes inside a block of size 1 free'd 
==82331== at 0x4C2C2BC: operator delete(void*) 
==82331== by 0x40095F: main 

Как можно обнаружить ошибки первого рода, возможно с помощью инструмента Valgrind?

ответ

3

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

Но они не могут поймать 100% из них.

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

  1. Использование интеллектуальных указателей вместо ссылок и указателей. По контракту вы можете доказать, что использование интеллектуальных указателей приводит к тому, что ссылки на объекты, выходящие за рамки, становятся логически невозможными.

  2. Использование итераторов и стандартных библиотечных алгоритмов, а не классический подход for (size_t i=0; i<container.size(); ++i). С четко определенными начальными и конечными итераторами, заканчивающимися концами массивов, становится логически невозможным. Кроме того, в качестве дополнительного бонуса код потребует меньших изменений, если по какой-либо причине выбор контейнеров будет переключен.

В вашем случае это практически невозможно для инструмента статического анализа, предназначенного только для выполнения, для его обнаружения. Конечный скомпилированный код не содержит абсолютно ничего, что во время выполнения официально отмечает временный выход из области видимости. Сгенерированный код выделяет фрейм стека, достаточный для размещения как переменной с автоматическим диапазоном, так и временным, который передается в качестве параметра. По завершении вызова конструктора явный вызов не генерируется, чтобы пометить временное как уничтоженное. Я не вижу, как valgrind, или любой другой инструмент статического анализа, возможно, знал об этом.

Возможно, если класс временного класса имеет явный деструктор, теоретически возможно, чтобы универсальный инструмент статического анализа знал, что экземпляр класса теперь уничтожен, благодаря его вызвавшемуся деструктору.

Но это показывает вам, что нет идеального ответа. Даже упомянутые мною практики программирования также не предотвращают 100% проблем; и они иногда вводят свою собственную сложность, которая должна быть принята во внимание (например, круговые ссылки при использовании интеллектуальных указателей).

+6

'valgrind' не является инструментом статического анализа. – immibis

+0

Я думаю, что это всего лишь урок предмета (каламбур), что неплохо хранить переданные ссылки любым способом, который сохраняется дольше, чем аргумент функции, в котором они передаются. Для любого вызова переданная ссылка может ссылаться на временный объект, и по возвращении он будет ссылаться на недопустимый объект, и тогда член будет бесполезен. Возможно, компиляторы должны быть созданы, чтобы генерировать предупреждение для такой ситуации. –

+0

@BlairHoughton - Как вы предлагаете компилятору при компиляции вызова функции, который передает ссылку на временный параметр, может выяснить, что вызываемая функция хранит переданную ссылку где-нибудь, когда компилятор, возможно, никогда не компилировал исходный код вызываемой функции, в любой момент в известной записанной истории, так как время начала эоны назад? –

Смежные вопросы