Подтвердить что ты не робот

Отключить "if (0)" исключение в gcc

Как я могу запретить GCC исключать код внутри , если (0) заблокирован?

Когда я использую Visual Studio, один из моих методов отладки заключается в том, чтобы поместить такой код в мою программу:

if (0)
    do_some_debug_printing_and_checking();

Затем, когда ударяется точка останова, я нажимаю на строку do_some_debug_printing_and_checking(), выбираю "установить следующую инструкцию" и заставляю ее выполнять.

Когда я использую gcc/gdb в качестве исходного кода, "set next statement" больше не работает, поскольку GCC просто удаляет код из инструкции if (0).

Я использую флаг -O0, чтобы отключить оптимизацию. Я также попробовал флаги -fno-dce -fno-tree-dce, чтобы явно отключить исключение скрытого кода, но это не влияет: содержимое if (0) просто не присутствует в двоичном файле, и я не могу использовать установить следующий оператор, чтобы перейти в него.

Есть ли хороший способ сказать gcc отключить удаление содержимого if (0)?

Edit:

Спасибо за обходной путь "дополнительная переменная", однако мне не нравятся 2 вещи:

  • Это еще одна строка кода
  • Он не будет автоматически оптимизирован, когда я создам версию выпуска и хочу, чтобы эти отладочные вещи исчезли. Конечно, я могу использовать # ifdef-s, но это еще больше дополнительных строк.

Действительно, нет никакого способа заставить GCC сохранить этот мертвый код?

4b9b3361

Ответ 1

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

например.

extern bool debug;
if (debug)
    do_some_debug_printing_and_checking();

Где-то в области пространства имен:

bool debug = false;

Ответ 2

Я бы не полагался на флагов компилятора gcc для этого. Флаги компилятора могут изменяться в разных версиях gcc и изменять их в компиляторах. Возможно, вам придется отлаживать один и тот же код за шесть месяцев в Visual С++...

@CharlesBailey дает хорошее представление о том, как это сделать с помощью переменной extern. Здесь есть одна альтернатива, которая не требует, чтобы переменная подвергалась воздействию всего модуля или хранилась в статическом хранилище.

Объявить временную переменную volatile в области оператора if:

if (volatile bool dbg = false)
{
  do_some_debug_printing_and_checking();
}

Это позволяет ограничить область действия временной переменной. Определитель volatile не позволяет компилятору ничего не понимать о переменной или оптимизировать ветвь.

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

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

#ifndef IS_DEBUGGING
#  define IS_DEBUGGING 0
#endif

#if IS_DEBUGGING
#  define TMP_DBG_FLAG volatile bool dbg_flag = false
#else
#  define TMP_DBG_FLAG false
#endif

Затем объявите оператор if следующим образом:

if ( TMP_DBG_FLAG )
{
  do_some_debug_printing_and_checking();
}

Когда вы определяете IS_DEBUGGING как 1, создается локальная переменная, объявляется изменчивой и сохраняется. Когда вы определяете значение IS_DEBUGGING равным 0, макрос расширяется до константы false, и компилятор оптимизирует ветвь. Что-то очень похожее можно было бы сделать и для подхода extern.

Это несколько дополнительных строк кода, но они не зависят от количества раз, когда вы используете TMP_DBG_FLAG. Код также намного читабельнее, чем использование т ifdef s. Макрос можно сделать немного безопаснее (добавив к нему значение __LINE__), но для этого потребовалось бы три макроса и, вероятно, не нужно:

#if IS_DEBUGGING
// paste symbols 'x' and 'y' together
#  define TMP_DBG_FLAG_SYMCAT0(x,y) x ## y

// need one level of indirection to expand __LINE__...
#  define TMP_DBG_FLAG_SYMCAT(x,y) TMP_DBG_FLAG_SYMCAT0(x,y)

#  define TMP_DBG_FLAG volatile bool TMP_DBG_FLAG_SYMCAT(dbg_flag_,__LINE__) = false
#else
#  define TMP_DBG_FLAG false
#endif