Есть ли предопределенный идентификатор для того, насколько вложенным является вызов функции? - PullRequest
0 голосов
/ 04 сентября 2018

Чтобы отладить рекурсивную программу, я считаю полезным визуализировать, насколько глубоко вложены мои вызовы функций. Я хотел бы иметь что-то вроде __func__, но за то, насколько глубока моя трассировка стека, а не то, как называется моя функция. Я понимаю, что для компилятора было бы невозможно просто знать об этом, потому что то, как вы вложены, является динамически генерируемым значением. Но для компилятора не составит труда добавить возможности в реализовать , вы можете просто добавить 1 к глобальному счетчику перед каждым call и вычесть 1 перед тем, как съесть ret.


Я использую следующий оператор отладки (найдено здесь ):

#define printdbg(Level, formatString, ...)                          \
    do {                                                            \
        fprintf(stderr, "%s:%d %s: " formatString "\n",             \
            __FILE__, __LINE__, __func__, ##__VA_ARGS__);           \
        if (Level == LEVEL_ERROR) { printf("quitting\n"); exit(1); }\
    } while (0)

Я хотел бы добавить дополнительный предопределенный идентификатор в начале, где я могу использовать что-то в форме printf("%*s", __NEST__+1, ":"), чтобы печатать в общей сложности __NEST__ пробелов в начале каждого оператора отладки, чтобы позволить мне визуализировать как глубоко в стеке был сделан каждый оператор отладки.


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

Я не могу найти список поддерживаемых предопределенных идентификаторов в Интернете. Все, что я нашел, это это и это , ни один из которых не претендует на полноту. Если существует эквивалент __NEST__, кто-то здесь, вероятно, знает одно слово, которое я ищу. Если его нет, то где я могу найти хорошо документированный список всех предопределенных идентификаторов?

1 Ответ

0 голосов
/ 09 сентября 2018

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

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

#include <stdio.h>

// Base nesting level (must be initialized by main)
static char *main_nesting;

// Return an integer corresponding to the current function call nesting level
int
nesting_level(void)
{
    int a;
    return (main_nesting - (char *)&a);
}

void
nest3(void)
{
    printf("%s=%d\n", __func__, nesting_level());
}

void
nest2(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest3();
}

void
nest1(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest2();
}

int
main(int argc, char *argv[])
{
    main_nesting = (char *)&argc;
    printf("%s=%d\n", __func__, nesting_level());
    nest1();
}

Когда вы запустите эту программу, вы получите следующий вывод:

main=20
nest1=68
nest2=116
nest3=164

Набор предопределенных имен макросов указан в § 6.10.8 стандарта ISO / IEC 9899: 2018 C . Кроме того, для GCC-подобных компиляторов вы можете получить список всех предопределенных макросов, выполнив следующую команду в системе Unix.

cpp -dM /dev/null
...