Программный способ получить имя переменной в C? - PullRequest
31 голосов
/ 26 октября 2009

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

Мое решение: сохранить имя переменной в виде строки и вывести «имя переменной», а затем ее значение.

Есть ли программный способ узнать имя переменной?

Ответы [ 10 ]

51 голосов
/ 26 октября 2009

Вы можете попробовать что-то вроде этого:

#define DUMP(varname) fprintf(stderr, "%s = %x", #varname, varname);

Раньше я использовал этот заголовок Я писал, когда я был новичком в C, он мог содержать некоторые полезные идеи. Например, это позволит вам напечатать значение C и предоставить спецификатор формата в одном (а также некоторую дополнительную информацию):

#define TRACE(fmt, var) \
        (error_at_line(0, 0, __FILE__, __LINE__, "%s : " fmt, #var, var))

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

11 голосов
/ 26 октября 2009

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

6 голосов
/ 26 октября 2009

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

#include <stdio.h>
#include <stdlib.h>

static void dumpMem (unsigned char *p, unsigned int s) {
    int i;
    unsigned char c[0x10];
    printf (">>      ");
    for (i = 0; i < 0x10; i++) printf (" +%x",i);
    printf (" +");
    for (i = 0; i < 0x10; i++) printf ("%x",i);
    printf ("\n");
    for (i = 0; i < ((s + 15) & 0xfff0); i++) {
        if ((i % 0x10) == 0) {
            if (i != 0) printf ("  %*.*s\n", 0x10, 0x10, c);
            printf (">> %04x ",i);
        }
        if (i < s) {
            printf (" %02x", p[i]);
            c[i & 0xf] = ((p[i] < 0x20) || (p[i] > 0x7e)) ? '.' : p[i];
        } else {
            printf ("   ");
            c[i & 0xf] = ' ';
        }
    }
    printf ("  %*.*s\n", 0x10, 0x10, c);
}
#define DUMPINT(x) do{printf("%s: %d\n",#x,x);dumpMem((char*)(&x),sizeof(int));}while(0)
#define DUMPSTR(x) do{printf("%s: %s\n",#x,x);dumpMem(x,strlen(x));}while(0)
#define DUMPMEM(x,s) do{printf("%s:\n",#x);dumpMem((char*)(&x),s);}while(0)

typedef struct {
    char c;
    int i;
    char c2[6];
} tStruct;

int main (void) {
    int i = 42;
    char *s = "Hello there, my name is Pax!";
    tStruct z;
    z.c = 'a'; z.i = 42; strcpy (z.c2,"Hello");

    DUMPINT (i);
    DUMPSTR (s);
    DUMPMEM (z,sizeof(z));

    return 0;
}

Это выводит:

i: 42
>>       +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +0123456789abcdef
>> 0000  2a 00 00 00                                      *...
s: Hello there, my name is Pax!
>>       +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +0123456789abcdef
>> 0000  48 65 6c 6c 6f 20 74 68 65 72 65 2c 20 6d 79 20  Hello there, my
>> 0010  6e 61 6d 65 20 69 73 20 50 61 78 21              name is Pax!
z:
>>       +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +0123456789abcdef
>> 0000  61 b6 16 61 2a 00 00 00 48 65 6c 6c 6f 00 0d 61  a..a*...Hello..a

И, если вас интересует здравый смысл do {...} while (0) в макросах, это позволяет ему размещаться в любом месте кода, не беспокоясь о том, достаточно ли у вас скобок вокруг него.

5 голосов
/ 29 сентября 2016

Короткий путь:

#define GET_VARIABLE_NAME(Variable) (#Variable)

тест:

#include <string>
class MyClass {};


int main(int argc, char* argv[]) {
    int foo = 0;

    std::string var_name1 = GET_VARIABLE_NAME(foo);
     char* var_name2 = GET_VARIABLE_NAME(foo);
     char* var_name3 = GET_VARIABLE_NAME(MyClass);


    return 0;
}
4 голосов
/ 26 октября 2009

Если вам нужно сделать это для произвольных переменных, вам, вероятно, придется использовать API-интерфейс отладчика, предоставляемый компилятором или платформой (например, DbgHelp в Windows).

В некоторых встроенных системах, над которыми я работал, нам нужно было иметь возможность отображать по команде значения некоторых важных переменных, которые известны заранее, и для этого все, что нам нужно, - это простая таблица Имя / указатель:

typedef 
struct vartab {
    char const* name;
    int * var;
} vartab;


vartab varTable[] = {
    { "foo", &foo },
    { "bar", &bar }
};

Тогда я просто использовал небольшую подпрограмму, которая ищет в таблице имя переменной, о которой идет речь, и выводит имя и данные, на которые указывает указатель. Если вам нужно вывести данные, отличные от обычных целочисленных значений, вы можете расширить структуру, чтобы она также содержала форматтер в стиле printf и изменить указатель на void* и передать этот мусор на snprintf() или что-то другое для форматирования данных.

Иногда я также использую макрос, который помогает построить таблицу (возможно, также объявив переменную). Но, честно говоря, я думаю, что это на самом деле только усложняет понимание (особенно для тех, кто присоединяется к проекту новичок - у них часто есть небольшой момент "WTF?") И на самом деле не сильно упрощает вещи.

3 голосов
/ 02 ноября 2010

Люди часто хотят, чтобы программы были самоанализом (или «отражением» в современном жаргоне). Но большинство языков программирования предлагают мало (например, Java) или нет (C) возможности отражать все детали программы (имена переменных, имена функций, типы, структуры выражений и т. Д.).

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

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

2 голосов
/ 06 сентября 2016

Попробуйте это.

#define MACRO_VARIABLE_TO_STRING(Variable) ((void) Variable,#Variable)

Код (void) Переменная - это void-преобразование, которое не работает, тогда есть оператор запятой и макрос stringify. Таким образом, чистый результат - const char *, содержащий имя переменной с проверкой компилятора, переменная действительно существует.

Теперь у вас есть имя вашей переменной, и вы можете использовать printf (), чтобы напечатать ее значение.

1 голос
/ 17 февраля 2019

Несмотря на очень точный ответ от @ Matt Joiner , я хочу написать полный короткий код , иллюстрирующий это только для того, чтобы увидеть, насколько просто он использует # define макро

#include <stdio.h>
#define dump(v)printf("%s",#v);
int main()
    {
    char a;
    dump(a);
    }

вывод: a

1 голос
/ 26 октября 2009

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

1 голос
/ 26 октября 2009

Если ваш исполняемый файл скомпилирован с отладочной информацией, вы можете получить эту информацию. Если нет, то вам, вероятно, не повезло. Так вы строите отладчик? Зачем? Существующие отладчики для c очень зрелые. Почему бы не использовать существующие инструменты вместо того, чтобы заново изобретать колесо?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...