Как я могу узнать, какие части в коде никогда не используются?

312

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

Как я могу узнать, какой код никогда не вызывается / никогда не используется?

user63898
источник
4
Я думаю, что язык запросов кода даст вам лучшее представление о вашем проекте в целом. Я не уверен в мире c ++, но, кажется, есть cppdepend.com (это не бесплатно), который выглядит достаточно прилично. Может быть что-то подобное может быть доступно бесплатно. Другое дело, прежде чем делать какой-либо рефакторинг, разумно было бы провести модульные тесты, если у вас их нет прямо сейчас. С помощью модульных тестов вы можете сделать так, чтобы ваши инструменты покрытия кода профилировали ваш код, что само по себе поможет удалить мертвый код, если вы не можете покрыть этот код.
Бисванат
3
Проверьте ссылку здесь: en.wikipedia.org/wiki/Unreachable_code
Мартин Йорк,
6
Я нахожу похожую тему. stackoverflow.com/questions/229069/…
UmmaGumma
3
Да, одна из забавных вещей C ++ состоит в том, что удаление «неиспользуемых» функций может все еще изменить результат программы.
MSalters
1
@MSalters: Это интересно ... Для этого нам нужно было бы поговорить о том, какая функция в наборе перегрузки выбрана для данного вызова, верно? Насколько мне известно, если есть две названные функции f(), и вызов f()однозначно преобразуется в 1-ю, то невозможно разрешить этот вызов до 2-й, просто добавив 3-ю названную функцию f()- «худшее, что вы можете сделать» "добавив, что третья функция должна вызвать неоднозначность вызова и, следовательно, предотвратить компиляцию программы. Хотел бы (= в ужасе) увидеть контрпример.
j_random_hacker

Ответы:

197

Существует два варианта неиспользуемого кода:

  • локальный, то есть в некоторых функциях некоторые пути или переменные не используются (или используются, но бессмысленно, как написано, но никогда не читается)
  • глобальный: функции, которые никогда не вызываются, глобальные объекты, к которым никогда не осуществляется доступ

Для первого вида хороший компилятор может помочь:

  • -Wunused(GCC, Clang ) должен предупреждать о неиспользуемых переменных, неиспользуемый анализатор Clang был даже увеличен, чтобы предупреждать о переменных, которые никогда не читаются (даже если используются).
  • -Wunreachable-code(старый GCC, удален в 2010 году ) должен предупреждать о локальных блоках, к которым никогда не осуществляется доступ (это происходит с ранним возвратом или условиями, которые всегда оцениваются как true)
  • я не знаю ни одной опции для предупреждения о неиспользуемых catchблоках, потому что компилятор обычно не может доказать, что не будет выброшено исключение

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

Поэтому существует два подхода:

  • Теоретическим является использование статического анализатора. Кусочек программного обеспечения, который детально изучит весь код и найдет все пути потока. На практике я не знаю ни одного, который бы работал здесь.
  • Прагматичным является использование эвристики: используйте инструмент покрытия кода (в цепочке GNU это gcov. Обратите внимание, что во время компиляции должны быть переданы определенные флаги, чтобы он работал правильно). Вы запускаете инструмент покрытия кода с хорошим набором различных входных данных (ваши юнит-тесты или нерегрессионные тесты), мертвый код обязательно находится в недоступном коде ... и поэтому вы можете начать отсюда.

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

  1. Используйте библиотеку Clang для получения AST (абстрактного синтаксического дерева)
  2. Выполните анализ меток и разметок, начиная с точек входа

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

Однако этот метод не может идентифицировать виртуальные переопределения, которые не используются, так как они могут быть вызваны сторонним кодом, о котором вы не можете рассуждать.

Матье М.
источник
7
Очень приятно +1. Мне нравится, что вы различаете код, который может быть определен статически, чтобы он никогда не выполнялся ни при каких обстоятельствах, и код, который не выполняется при конкретном запуске, но потенциально может. Я считаю, что первое является важным, и, как вы говорите, анализ достижимости с использованием AST всей программы - это способ получить его. (Предотвращение foo()пометки как «вызываемого», когда оно появляется только в, if (0) { foo(); }было бы бонусом, но требует дополнительных умов.)
j_random_hacker
@j_random_hacker: возможно, что использование CFG (Control-Flow Graph) было бы лучше сейчас, когда я об этом думаю (благодаря вашему примеру). Я знаю, что Clang стремится отметить тавтологические сравнения, подобные той, которую вы упомянули, и, таким образом, используя CFG, мы могли бы обнаружить мертвый код на ранней стадии.
Матье М.
@Matthieu: Да, возможно, CFG - это то, что я имею в виду, а не AST :) Я имею в виду: орграф, в котором вершины являются функциями, и есть грань от функции x до функции y всякий раз, когда x может вызвать y. (И с важным свойством, что перегруженные функции все представлены различными вершинами - похоже, что Clang сделает это за вас, фу!)
j_random_hacker
1
@j_random_hacker: на самом деле CFG является более сложным, чем простой орграф, поскольку он представляет весь код, который должен быть выполнен в блоках со ссылками из одного блока в другой на основе условных операторов. Основным преимуществом является то, что он естественным образом подходит для удаления кода, который статически может быть определен как мертвый (он создает недоступные блоки, которые могут быть идентифицированы), поэтому было бы лучше использовать CFG, чем AST, для создания используемого вами орграфа. говорить о ... я думаю :)
Матье М.
1
@j_random_hacker: на самом деле AST Clang делает, он делает все явным (или почти ...), потому что он предназначен для работы с кодом, а не просто для его компиляции. На самом деле сейчас идет обсуждение, потому что, очевидно, существует проблема со списками инициализаторов, когда такое неявное преобразование не появляется в AST, но я думаю, что это будет исправлено.
Матье М.
35

В случае неиспользуемых целых функций (и неиспользуемых глобальных переменных) GCC может фактически сделать большую часть работы за вас, при условии, что вы используете GCC и GNU ld.

При компиляции источника используйте -ffunction-sectionsи -fdata-sections, затем при компоновке используйте -Wl,--gc-sections,--print-gc-sections. Компоновщик теперь перечислит все функции, которые могут быть удалены, потому что они никогда не вызывались, и все глобальные переменные, на которые никогда не ссылались.

(Конечно, вы также можете пропустить --print-gc-sectionsчасть и позволить компоновщику удалить функции без уведомления, но оставьте их в источнике.)

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

Некоторые специфичные для C ++ функции также могут вызвать проблемы, в частности:

  • Виртуальные функции. Не зная, какие подклассы существуют, а какие на самом деле создаются во время выполнения, вы не можете знать, какие виртуальные функции вам нужны в конечной программе. У компоновщика недостаточно информации об этом, поэтому он должен держать их всех вокруг.
  • Глобалы с конструкторами и их конструкторы. В общем, компоновщик не может знать, что конструктор для глобала не имеет побочных эффектов, поэтому он должен его запустить. Очевидно, это означает, что само глобальное тоже необходимо сохранить.

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

Дополнительным предостережением является то, что если вы создаете общую библиотеку, настройки по умолчанию в GCC будут экспортировать каждую функцию в общей библиотеке, что приведет к ее «использованию» в отношении компоновщика. Чтобы это исправить, вам нужно установить по умолчанию скрытие символов вместо экспорта (используя, например -fvisibility=hidden), а затем явно выбрать экспортируемые функции, которые нужно экспортировать.

olsner
источник
Отличный практический совет. Просто получение списка функций, о которых известно, что они нигде не используются (даже если, как вы говорите, этот список неполон), я думаю, принесет много пользы.
j_random_hacker
Я не думаю, что все это работает для необоснованных шаблонов .
Якуб Клинковский
25

Хорошо, если вы используете g ++, вы можете использовать этот флаг -Wunused

По документации:

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

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Изменить : Вот другой полезный флаг -Wunreachable-code Согласно документации:

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

Обновление : я нашел похожую тему Обнаружение мертвого кода в устаревшем проекте C / C ++

Ummagumma
источник
4
Это не поймает заголовки прототипа функций, которые никогда не вызываются. Или методы открытого класса, которые не вызываются. Он может только проверять, используются ли локальные переменные в этой области.
Фалмарри
@Falmarri Я никогда не использовал этот флаг. Я пытаюсь понять, какие мертвые коды я могу найти с помощью этого.
UmmaGumma
-Wunusedпредупреждает о переменных, которые объявлены (или объявлены и определены за один раз), но фактически никогда не используются. Кстати, это довольно раздражает охранников с ограниченным доступом: p В Clang есть экспериментальная реализация, которая также предупреждает о энергонезависимых переменных, которые записываются, но никогда не читаются (Тедом Кременеком). -Wunreachable-codeпредупреждает о коде внутри функции , которая не может быть достигнута, это может быть код расположен после throwили returnзаявления или коды в отрасли , который никогда не принимал (что случается в случае тавтологических сравнений), например.
Матье М.
18

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

Вы можете попробовать дать этому инструменту покрытия с открытым исходным кодом шанс: TestCocoon - инструмент покрытия кода для C / C ++ и C #.

Карлос V
источник
7
Ключ здесь «как он работает» - если ваши входные данные не используют какой-либо путь кода, этот путь не будет распознан как использованный, не так ли?
Sharp
1
Это верно. Без запуска кода невозможно узнать, какие строки не достигнуты. Интересно, насколько сложно будет настроить некоторые юнит-тесты для имитации нескольких обычных прогонов.
Карлос V
1
@drhishch Я думаю, что большинство такого неиспользуемого кода должно найти компоновщик, а не компилятор.
UmmaGumma
1
@drhirsch Правда, компилятор может позаботиться о некотором коде, который недоступен, например, о функциях, которые объявлены, но не вызваны, и о некоторых оценках короткого замыкания, но как насчет кода, который зависит от действий пользователя или переменных времени выполнения?
Карлос V
1
@golcarcol Хорошо, теперь у нас есть функция void func()в a.cpp, которая используется в b.cpp. Как компилятор может проверить, что func () используется в программе? Это работа линкеров.
UmmaGumma
15

Реальный ответ здесь: вы никогда не можете знать наверняка.

По крайней мере, для нетривиальных случаев вы не можете быть уверены, что получили все это. Рассмотрим следующее из статьи Википедии о недоступном коде :

double x = sqrt(2);
if (x > 5)
{
  doStuff();
}

Как правильно отмечает Википедия, умный компилятор может поймать что-то подобное. Но рассмотрим модификацию:

int y;
cin >> y;
double x = sqrt((double)y);

if (x != 0 && x < 1)
{
  doStuff();
}

Компилятор поймает это? Может быть. Но для этого нужно будет сделать больше, чем просто запустить sqrtскалярное значение. Придется выяснить, что (double)yвсегда будет целым числом (легко), а затем понять математический диапазон sqrtдля набора целых чисел (трудно). Очень сложный компилятор может сделать это для sqrtфункции, или для каждой функции в math.h , или для любой функции с фиксированным вводом, чью область он может выяснить. Это становится очень, очень сложным, и сложность в основном безгранична. Вы можете продолжать добавлять уровни сложности к своему компилятору, но всегда будет способ проникнуть в некоторый код, который будет недоступен для любого данного набора входных данных.

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

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

ИМХО, единственное реальное решение - это быть как можно бдительнее, использовать автоматизацию в вашем распоряжении, проводить рефакторинг, где вы можете, и постоянно искать способы улучшить свой код. Конечно, в любом случае это хорошая идея.

Джастин морган
источник
1
Правда и не оставляйте мертвый код в! Если вы удалите функцию, убейте мертвый код. Если оставить его «на всякий случай», это приведет к раздутию, которое (как вы уже обсуждали) трудно найти позже. Пусть контроль версий делает накопление для вас.
Гонки
12

Я не использовал его сам, но cppcheck утверждает, что нашел неиспользуемые функции. Это, вероятно, не решит полную проблему, но это может быть началом.

Мистер акула
источник
Да, он может найти локальные не связанные переменные и функции.
Chugaister
Да, используйте, cppcheck --enable=unusedFunction --language=c++ .чтобы найти эти неиспользуемые функции.
Джейсон Харрис
9

Вы можете попробовать использовать PC-lint / FlexeLint от Gimple Software . Утверждает, что

найти неиспользуемые макросы, typedef, классы, члены, объявления и т. д. во всем проекте

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

Тони
источник
5

Мой обычный подход к поиску неиспользуемых вещей

  1. убедитесь, что система сборки правильно обрабатывает отслеживание зависимостей
  2. настройте второй монитор с полноэкранным окном терминала, запустите повторные сборки и покажите первый скриншот вывода. watch "make 2>&1"имеет тенденцию делать трюки на Unix.
  3. выполнить операцию поиска и замены для всего дерева исходных текстов, добавив "??" в начале каждой строки
  4. исправить первую ошибку, помеченную компилятором, удалив "//?" в соответствующих строках.
  5. Повторяйте, пока не останется ошибок.

Это довольно длительный процесс, но он дает хорошие результаты.

Саймон Рихтер
источник
2
Имеет заслуги, но очень трудоемкий. Также вы должны убедиться, что одновременно раскомментированы все перегрузки функции - если применимо более одной, раскомментирование менее предпочтительной может привести к успешной компиляции, но приведет к некорректному поведению программы (и неверному представлению о том, какой функции используются).
j_random_hacker
Я только раскомментирую объявления на первом шаге (все перегрузки), а на следующей итерации вижу, какие определения отсутствуют; таким образом, я могу видеть, какие перегрузки действительно используются.
Саймон Рихтер
@Simon: Интересно, что в комментарии к основному вопросу MSalters указывает, что даже наличие / отсутствие объявления для функции, которая никогда не вызывается, может повлиять на то, какая из двух других функций найдена разрешением перегрузки. По общему признанию это требует чрезвычайно причудливой и искусной настройки, таким образом, это вряд ли будет проблемой на практике.
j_random_hacker
4

Отметьте как можно больше открытых функций и переменных как частные или защищенные, не вызывая ошибки компиляции, при этом попробуйте также выполнить рефакторинг кода. Сделав функции частными и в некоторой степени защищенными, вы сократили область поиска, поскольку частные функции можно вызывать только из одного и того же класса (если только нет глупых макросов или других хитростей, позволяющих обойти ограничение доступа, и если это так, я бы порекомендовал вам найти новую работу). Намного легче определить, что вам не нужна закрытая функция, поскольку только класс, над которым вы сейчас работаете, может вызывать эту функцию. Этот метод проще, если ваша кодовая база имеет небольшие классы и слабо связана. Если ваша кодовая база не имеет небольших классов или имеет очень тесную связь, я предлагаю сначала очистить их.

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

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

Ли Райан
источник
3

Если вы работаете в Linux, вам может понадобиться callgrindинструмент для анализа программ на C / C ++, который входит в valgrindкомплект, который также содержит инструменты, которые проверяют утечки памяти и другие ошибки памяти (которые вы также должны использовать). Он анализирует работающий экземпляр вашей программы и выдает данные о графе вызовов и о затратах на производительность узлов в графе вызовов. Обычно он используется для анализа производительности, но также создает график вызовов для ваших приложений, чтобы вы могли видеть, какие функции вызываются, а также их вызывающих.

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

Адам Хигера
источник
3

Я действительно не использовал ни одного инструмента, который бы делал такие вещи ... Но, насколько я видел во всех ответах, никто никогда не говорил, что эта проблема неисчислима.

Что я имею в виду под этим? Эта проблема не может быть решена никаким алгоритмом на компьютере. Эта теорема (что такого алгоритма не существует) является следствием проблемы останова Тьюринга.

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

geekazoid
источник
1
Я думаю, что OP в основном хочет найти функции, которые нигде не вызываются, что, безусловно, не является вычислимым - большинство современных компоновщиков могут это сделать! Это просто вопрос извлечения этой информации с наименьшим количеством боли и тяжелой работы.
j_random_hacker
Вы правы, я не видел последний комментарий к основному вопросу. Кстати, в коде могут быть функции, которые на самом деле не используются. Такие вещи не могут быть обнаружены.
geekazoid
2

Одним из способов является использование отладчика и функции компилятора для устранения неиспользуемого машинного кода во время компиляции.

Как только некоторый машинный код будет удален, отладчик не позволит вам поставить точку останова на соответствующей строке исходного кода. Таким образом, вы ставите точки останова повсюду, запускаете программу и проверяете точки останова - те, которые находятся в состоянии «код не загружен для этого источника», соответствуют исключенному коду - либо этот код никогда не вызывается, либо он был встроен, и вам нужно выполнить какой-то минимум анализ, чтобы найти, какой из этих двух произошло.

По крайней мере, так работает в Visual Studio, и я думаю, что другие наборы инструментов также могут это делать.

Это много работы, но я думаю, быстрее, чем анализировать весь код вручную.

Sharptooth
источник
4
Я думаю, что вопрос ОП заключается в том, как найти меньшее, более управляемое подмножество исходного кода, а не в том, чтобы убедиться, что скомпилированный двоичный файл эффективен.
j_random_hacker
@j_random_hacker Я дал это хотя - и оказывается, что устранение кода может даже использоваться для отслеживания назад к оригинальному исходному коду.
Sharp
У вас есть какие-то конкретные флаги компилятора в Visual Studio, чтобы достичь этого? и работает ли он только в режиме релиза или в отладке?
Навин
Как насчет строк, которые используются, но оптимизированы компилятором?
Итамар Кац
@Naveen: В Visual C ++ 9 вы должны включить оптимизацию и использовать / OPT: ICF
sharptooth
2

CppDepend - это коммерческий инструмент, который может обнаруживать неиспользуемые типы, методы и поля и делать гораздо больше. Он доступен для Windows и Linux (но в настоящее время не имеет 64-битной поддержки) и поставляется с двухнедельной пробной версией.

Отказ от ответственности: я не работаю там, но у меня есть лицензия на этот инструмент (а также NDepend , который является более мощной альтернативой для кода .NET).

Для тех, кому интересно, вот пример встроенного (настраиваемого) правила для обнаружения мертвых методов, написанного на CQLinq :

// <Name>Potentially dead Methods</Name>
warnif count > 0
// Filter procedure for methods that should'nt be considered as dead
let canMethodBeConsideredAsDeadProc = new Func<IMethod, bool>(
    m => !m.IsPublic &&       // Public methods might be used by client applications of your Projects.
         !m.IsEntryPoint &&            // Main() method is not used by-design.
         !m.IsClassConstructor &&      
         !m.IsVirtual &&               // Only check for non virtual method that are not seen as used in IL.
         !(m.IsConstructor &&          // Don't take account of protected ctor that might be call by a derived ctors.
           m.IsProtected) &&
         !m.IsGeneratedByCompiler
)

// Get methods unused
let methodsUnused = 
   from m in JustMyCode.Methods where 
   m.NbMethodsCallingMe == 0 && 
   canMethodBeConsideredAsDeadProc(m)
   select m

// Dead methods = methods used only by unused methods (recursive)
let deadMethodsMetric = methodsUnused.FillIterative(
   methods => // Unique loop, just to let a chance to build the hashset.
              from o in new[] { new object() }
              // Use a hashet to make Intersect calls much faster!
              let hashset = methods.ToHashSet()
              from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods)
              where canMethodBeConsideredAsDeadProc(m) &&
                    // Select methods called only by methods already considered as dead
                    hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe
              select m)

from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain)
select new { m, m.MethodsCallingMe, depth = deadMethodsMetric[m] }
Роман Бойко
источник
Обновление: 64-битная поддержка для Linux была добавлена ​​в версии 3.1.
Роман Бойко
1

Это зависит от платформы, которую вы используете для создания приложения.

Например, если вы используете Visual Studio, вы можете использовать такой инструмент, как .NET ANTS Profiler, который может анализировать и профилировать ваш код. Таким образом, вы должны быстро узнать, какая часть вашего кода фактически используется. Eclipse также имеет эквивалентные плагины.

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

Для каждой основной функции вы можете отследить ее использование, и через несколько дней / недель просто получите этот файл журнала и посмотрите на него.

AUS
источник
1
.net ANTS Profiler выглядит так, как будто он для C # - вы уверены, что он работает и для C ++?
j_random_hacker
@j_random_hacker: насколько я знаю, он работает с управляемым кодом. Поэтому .net ANTS, конечно, не сможет анализировать «стандартный» код C ++ (т. Е. Скомпилированный с помощью gcc, ...).
AUS
0

Я не думаю, что это можно сделать автоматически.

Даже с инструментами покрытия кода вы должны предоставить достаточные входные данные для запуска.

Может быть очень сложным и дорогостоящим инструментом статического анализа, таким как компилятор Coverity или LLVM .

Но я не уверен и предпочел бы ручную проверку кода.

ОБНОВЛЕНО

Ну .. только удаление неиспользуемых переменных, неиспользуемые функции не сложно, хотя.

ОБНОВЛЕНО

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

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

9dan
источник
2
формулировка вашего ответа вводит в заблуждение, в LLVM нет ничего дорогого ... это бесплатно!
Матье М.
ручное редактирование не поможет вам с переменными времени выполнения, которые проходят через логику ветвей в вашей программе. Что если ваш код никогда не соответствует определенным критериям и поэтому всегда следует по одному и тому же пути?
Карлос V
0

У меня был друг, который задавал мне этот вопрос сегодня, и я посмотрел вокруг на несколько многообещающих разработок Clang, например, ASTMatcher и Static Analyzer, которые могут иметь достаточную видимость в процессе компиляции для определения разделов мертвого кода, но затем я нашел это:

https://blog.flameeyes.eu/2008/01/today-how-to-identify-unused-exported-functions-and-variables

Это в значительной степени полное описание того, как использовать несколько флагов GCC, которые, по-видимому, предназначены для идентификации символов, на которые нет ссылок!

Стивен Лу
источник
0

Общая проблема, если какая-то функция будет вызвана, является NP-Complete. Вы не можете заранее знать, будет ли вызвана какая-либо функция, поскольку вы не будете знать, остановится ли когда-нибудь машина Тьюринга. Вы можете получить, если есть некоторый (статически) путь, который идет от main () к функции, которую вы написали, но это не гарантирует, что она когда-либо будет вызвана.

Луис Колорадо
источник
-3

Хорошо, если вы используете g ++, вы можете использовать этот флаг -Wunused

По документации:

Warn whenever a variable is unused aside from its declaration, whenever a function is declared static but never defined, whenever a label is declared but not used, and whenever a statement computes a result that is explicitly not used.

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Изменить: Вот другой полезный флаг -Wunreachable-код Согласно документации:

This option is intended to warn when the compiler detects that at least a whole line of source code will never be executed, because some condition is never satisfied or because it is after a procedure that never returns.
Рам Сингх
источник
6
Эта точная информация уже упоминалась в наиболее актуальном ответе. Пожалуйста, прочитайте существующие ответы, чтобы избежать ненужного дублирования.
j_random_hacker
1
Теперь вы можете заработать свой значок давления со стороны сверстников!
Эндрю Гримм