39

我正在开发一种工具来从变量中转储数据。我需要转储变量名以及值。

我的解决方案:将变量名存储为字符串,并打印“变量名”,后跟它的值。

是否有任何编程方式可以知道变量名?

4

10 回答 10

60

你可以尝试这样的事情:

#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++,则可以使用传递值的类型并适当地输出它。如果是这种情况,我可以提供一个更有利可图的示例来说明如何“漂亮地打印”变量值。

于 2009-10-26T04:56:28.190 回答
11

在 C 中,变量名在编译步骤(和链接步骤,如果变量是全局的)期间存在,但在运行时不可用。您必须选择包含指示变量名称的文字字符串的解决方案。

于 2009-10-26T04:47:23.260 回答
10

更短的方式:

#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;
}
于 2016-09-29T16:36:02.580 回答
7

我实际上有一些代码可以做你想要的。它使用预处理器对变量名称进行字符串化,以便您将其打印出来。它转储变量名称和值(基于类型)以及该变量的内存布局。以下程序显示了它是如何完成的:

#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)宏的完整性,那就是让它可以放在代码中的任何地方,而不必担心是否有足够的大括号围绕它。

于 2009-10-26T05:39:19.897 回答
4

如果您需要对任意变量执行此操作,那么您可能需要使用编译器或平台提供的调试器 API(如 Windows 上的 DbgHelp)。

在我工作的一些嵌入式系统上,我们需要能够在命令中显示某些提前知道的重要变量的值,为此我们只需要一个简单的名称/指针表:

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


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

然后我只是使用了一个小例程,在表中搜索有问题的变量名,并转储名称和指针指向的数据。如果您需要转储除普通整数以外的数据,您可以扩展该结构以保存 printf 样式的格式化程序并将指针更改为 avoid*并将该垃圾传递给snprintf()或其他东西来格式化数据。

有时我还会使用一个宏来帮助构建表(可能还声明变量)。但老实说,我认为这真的只是让理解变得更加复杂(尤其是对于新加入该项目的人——他们通常有一个小的“WTF?”时刻)并且并没有真正简化事情。

于 2009-10-26T05:11:32.090 回答
3

人们经常希望程序能够自我反省(或用当前术语“反映”)。但是大多数编程语言几乎没有(例如Java)或没有(C)能力来反映程序的所有细节(变量名、函数名、类型、表达式结构等)。

对于所有语言,有一种方法可以做到这一点:跳出语言,使用旨在从语言中提取信息的工具。可以做到这一点的一类工具称为程序转换系统。

有关如何使用程序转换系统“获取变量名”和打印值的讨论,请参阅此 SO 答案: 自动跟踪变量的更改

于 2010-11-02T09:15:30.703 回答
2

试试这个。

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

代码(void)变量是无操作的void转换,然后有逗号运算符和宏字符串化。所以最终结果是 const char* 包含变量名,编译器检查变量是否真的存在。

现在您有了变量的名称,您可以使用 printf() 来打印它的值。

于 2016-09-06T13:06:10.440 回答
1

如果你的可执行文件是用调试信息编译的,你可能会得到这个信息。如果没有,你可能不走运。所以你正在构建一个调试器?为什么?现有的 c 调试器非常成熟。为什么不使用现有工具而不是重新发明轮子?

于 2009-10-26T04:46:07.273 回答
1

恐怕没有一个好的方法可以从你的程序内部做到这一点(除了 Anacrolix 的回答)。我认为您的问题的正确解决方案是调试器脚本。^C在大多数调试器中,您甚至可以在每次调试器中断程序执行(断点、您点击等)时将其挂接到运行,并在每次与它交互时获取程序状态的快照。

于 2009-10-26T04:53:26.540 回答
1

尽管@Matt Joiner 给出了非常精确的答案,我还是想编写一个完整的简短代码来举例说明这一点,看看它使用#define宏有多简单

#include <stdio.h>
#define val_name(v)#v
int main()
    {
    char a[]="hello";
    printf("%s %s",a,val_name(a));
    }

输出:你好

于 2019-02-17T13:55:47.920 回答