我正在开发一个从变量转储数据的工具。 我需要转储变量名称,以及值。
我的解决方案:将变量名称存储为字符串,然后打印“变量名称”,后跟其值。
是否有任何编程方式来了解变量名称?
答案 0 :(得分:51)
您可以尝试这样的事情:
#define DUMP(varname) fprintf(stderr, "%s = %x", #varname, varname);
我曾经使用this header我写过,当我不熟悉C时,它可能包含一些有用的想法。例如,这将允许您打印C值并在一个中提供格式说明符(以及一些其他信息):
#define TRACE(fmt, var) \
(error_at_line(0, 0, __FILE__, __LINE__, "%s : " fmt, #var, var))
如果您使用的是C ++,则可以使用传递值的类型并适当地输出。如果是这种情况,我可以为如何“漂亮地打印”变量值提供一个更有利可图的例子。
答案 1 :(得分:11)
在C中,在编译步骤(和链接步骤,如果变量是全局的)中存在变量名,但在运行时不可用。您必须选择一个涉及表示变量名称的文字字符串的解决方案。
答案 2 :(得分:6)
我实际上有一些代码可以做你想要的。它使用预处理器对变量名进行字符串化,以允许您将其打印出来。它转储变量名称和值(基于类型)和该变量的内存布局。以下程序显示了它是如何完成的:
#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)
的完整性,那就是将它放在代码中的任何位置,而不必担心是否有足够的括号围绕它。
答案 3 :(得分:5)
缩短方式:
#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 :(得分:4)
如果您需要为任意变量执行此操作,那么您可能需要使用编译器或平台提供的调试器API(如Windows上的DbgHelp)。
在我工作的一些嵌入式系统上,我们需要能够在命令中显示提前知道的某些重要变量的值,并且为此我们需要的只是一个简单的名称/指针表:
typedef
struct vartab {
char const* name;
int * var;
} vartab;
vartab varTable[] = {
{ "foo", &foo },
{ "bar", &bar }
};
然后我只使用了一个小例程,在表中搜索有问题的变量名,并转储指针所指向的名称和数据。如果你需要转储除普通整数以外的数据,你可以扩展结构以保持printf样式格式化程序并将指针更改为void*
并将该垃圾传递给snprintf()
或其他格式数据。
有时我也会使用一个有助于构建表的宏(可能还会声明变量)。但说实话,我认为这实际上只是让理解更加复杂(特别是对于那些新加入项目的人来说 - 他们经常会有一个小小的“WTF?”时刻)而且并没有真正简化任何事情。
答案 5 :(得分:3)
人们经常希望程序能够自我反省(或用当前的术语“反思”)。但是大多数编程语言提供很少(例如,Java)或没有(C)能力来反映程序的所有细节(变量名,函数名,类型,表达式结构等)。
有一种方法可以为所有语言执行此操作:在外部语言中执行此操作,并使用旨在从该语言中提取该信息的工具。可以执行此操作的一类工具称为程序转换系统。
有关如何使用程序转换系统“获取变量名称”和打印值的讨论,请参阅此SO答案: Trace changes to variables automatically
答案 6 :(得分:2)
试试这个。
#define MACRO_VARIABLE_TO_STRING(Variable) ((void) Variable,#Variable)
Code(void)变量是无效的转换,然后是逗号运算符和宏字符串化。因此,最终结果是const char *包含变量名,编译器检查变量确实存在。
现在您拥有变量的名称,并且可以使用printf()来打印其值。
答案 7 :(得分:1)
如果使用调试信息编译可执行文件,则可能会获得此信息。如果没有,你可能运气不好。那么你正在构建一个调试器?为什么? c的现有调试器非常成熟。为什么不使用现有工具而不是重新发明轮子?
答案 8 :(得分:1)
在你的程序中没有一个好方法可以做到这一点,我担心(除了Anacrolix的回答)。我认为您的问题的正确解决方案是调试器脚本。在大多数调试器中,您甚至可以在每次调试器中断程序执行(断点,您点击^C
等)时将其连接起来运行,并在每次与它交互时获取程序状态的快照。
答案 9 :(得分:1)
尽管 @Matt Joiner 给出了非常准确的答案,我还是想编写一个完整的短代码,举例说明这一点,以了解使用 #define < / strong>宏
#include <stdio.h>
#define dump(v)printf("%s",#v);
int main()
{
char a;
dump(a);
}
输出: