我正在尝试通过检查局部变量和全局变量的值来测试一些用 C 编写的包,我尝试使用 GDB 调试器和 fprint,正如这里许多人所建议的那样,它们在简单的小程序中运行良好,但是有了这个包,这并不容易。
因此,我需要将所有变量提取到一个 txt.file 中(每一行都有一个变量),然后在运行程序时我需要打印这些变量的值。
我使用普通的打印语句从 txt 文件中获取变量的名称,问题是打印的是确切的字符。
问题:如何使用文本文件中的这些字符作为变量来打印值而不是名称?
变量.txt
x
y
d
主.c
在主文件中,我包含了 header 并调用了函数。
//printState.h
void printstate(){
char ch;
FILE *fp;
if(fp = fopen("Varaibles.txt", "r"))
{
ch=getc(fp);
while(ch != EOF)
{
printf("%c",ch);
ch = getc(fp);
}
fclose(fp);
}
}
int func(int x) {
int y = 0;
x = y + x;
if(x > 0){
x = x % 4;
printstate();
/* I want to know the value of x at this point.*/
}
else {
x = x + 1;
printstate();
/* I want to know the value of x at this point.*/
}
return x;
}
预期输出:
是语句 (x = x % 4)
和 (x = x + 1)
之后 x, y, d 的值
例如:
5
7
6
我得到的实际输出是:
x
y
d
最佳答案
这道题暗示了对局部变量和全局变量的反射的使用。可悲的是,C 没有这样的概念。
我确定 gdb 可以帮助您生成局部变量和全局变量的列表(也许是 here?)
但是,由于您没有明确说明如何实现这一目标,我将为此投入 2 美分。
有一些项目在 C 上实现了反射,但我更喜欢利用预处理器来实现这一点。尽管您可以避免使用仅 header 库,但我将使用 P99 来简化宏编程。
MWE 可以是以下一个:
#include <P99/p99_for.h>
#include <P99/p99_args.h>
#include <stdio.h>
enum print_type {
PT_char,
PT_int,
PT_ptr,
PT_string,
};
#define FOR_PAIR(CONTEXT, OP, FUNC, ...) P99_PASTE2(_BASE_FOR_PAIR_, P99_NARG(__VA_ARGS__))(CONTEXT, OP, FUNC, ## __VA_ARGS__)
#define _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, value1, value2) FUNC(CONTEXT, 0, value1, value2)
#define _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 1, FUNC(CONTEXT, 1, value1, value2), _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 2, FUNC(CONTEXT, 2, value1, value2), _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 3, FUNC(CONTEXT, 3, value1, value2), _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_10(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 4, FUNC(CONTEXT, 4, value1, value2), _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _LENGTH_VAR _localsTrackingSystem_arrayLengths
#define _NAMES_VAR _localsTrackingSystem_names
#define _VARIABLES_VAR _localsTrackingSystem_variables
#define _PRINTMETHOD_VAR _localsTrackingSystem_printMethod
#define STR(x) #x
#define _NAMES_REDUCE(NAME, I, REC, RES) REC, RES
#define _NAMES_MAP(context, length, type, name) (STR(name))
#define _GENERATE_NAMES(...) FOR_PAIR(, _NAMES_REDUCE, _NAMES_MAP, ## __VA_ARGS__)
#define _POINTERS_REDUCE(NAME, I, REC, RES) REC; RES
#define _POINTERS_MAP(arrayLength, length, type, aname) _VARIABLES_VAR[arrayLength - length - 1] = ((void*)&aname)
#define _GENERATE_POINTERS(...) FOR_PAIR(P99_DIV(P99_NARG(__VA_ARGS__), 2), _POINTERS_REDUCE, _POINTERS_MAP, ## __VA_ARGS__)
#define _PRINT_REDUCE(NAME, I, REC, RES) REC, RES
#define _PRINT_MAP(context, length, type, name) (P99_PASTE2(PT_, type))
#define _GENERATE_PRINT(...) FOR_PAIR(, _PRINT_REDUCE, _PRINT_MAP, ## __VA_ARGS__)
//variadic needs to be always even
// _GENERATE_POINTERS needs to be initialized every time since pointers may change (although length doesn't)
#define TRACKED_FUNCTION(...) \
static const int _LENGTH_VAR = P99_DIV(P99_NARG(__VA_ARGS__), 2); \
static const char* _NAMES_VAR[] = {_GENERATE_NAMES(__VA_ARGS__)}; \
static const enum print_type _PRINTMETHOD_VAR[] = {_GENERATE_PRINT(__VA_ARGS__)}; \
static const void* _VARIABLES_VAR[P99_DIV(P99_NARG(__VA_ARGS__), 2)]; \
_GENERATE_POINTERS(__VA_ARGS__)
#define printState() _printState(_LENGTH_VAR, _NAMES_VAR, _VARIABLES_VAR, _PRINTMETHOD_VAR);
void _printState(int length, const char** nameArray, const void** pointerArray, const enum print_type* printMethodArray) {
for (int i=0; i<length; ++i) {
printf("at %p %s = ", pointerArray[i], nameArray[i]);
switch (printMethodArray[i]) {
case PT_char: {
printf("%c", *((char*)pointerArray[i]));
break;
}
case PT_int: {
printf("%d", *((int*)pointerArray[i]));
break;
}
case PT_ptr: {
printf("%p", *((void**)pointerArray[i]));
break;
}
case PT_string: {
printf("%s", *((char**)pointerArray[i]));
break;
}
default: {
exit(1);
}
}
printf("\n");
}
}
int func(int x, const char* name){
//LOCALS DEFINITIONS
int y = 0;
int* yPtr = &y;
x = y + x;
//declare which variables you want to track... like your "variables.txt" files
TRACKED_FUNCTION(int, x, int, y, ptr, yPtr, string, name);
//MAIN BODY
if(x > 0) {
x = x % 4;
printf("expected x=%d, y=%d, yPtr=%p name=%s\n", x, y, yPtr, name);
printState();
/* I want to know the value of x at this point.*/
} else {
x = x + 1;
printf("expected x=%d, y=%d, yPtr=%p name=%s\n", x, y, yPtr, name);
printState();
/* I want to know the value of x at this point.*/
}
return x;
}
int main() {
func(5, "Hello World!");
}
我的输出:
expected x=1, y=0, yPtr=0x7ffec1e5b5ec name=Hello World!
at 0x7ffec1e5b5dc x = 1
at 0x7ffec1e5b5ec y = 0
at 0x7ffec1e5b5f0 yPtr = 0x7ffec1e5b5ec
at 0x7ffec1e5b5d0 name = Hello World!
我已经处理过类似的概念 here ,但是让我简要解释一下这段代码的作用:
- 用法:非常简单; 在声明了所有您有兴趣打印的变量后,您调用
TRACKED_FUNCTION
并传递变量对序列:变量对的第一个元素是一个字符串,表示您希望如何打印其内容变量(例如,您可能有一个char
,但也许您想将其打印为int
);第二个是变量本身的名称;所以在示例TRACKED_FUNCTION(int, x, int, y, ptr, yPtr, string, name);
中,我们要跟踪x
,它需要打印为int
,y
作为int
,yPtr
作为指针,name
作为字符串;调用此类宏后,您可以随时调用printState()
获取您显式声明的所有变量的列表; - 内部细节:我的主要想法是创建 4 个“隐藏”变量:一个包含所有变量名称的数组,另一个包含所有变量指针,另一个包含每个变量如何打印它和一个表示所有数组长度的整数;
TRACKED_FUNCTION
生成这 3 个数组,而printState()
只打印它们。特别是,TRACKED_FUNCTION
中每对的第一个元素实际上连接成属于枚举print_type
的标识符:这是我们可以使用“类型”string
但不能使用类型void*
的主要原因:PT_void*
不是一个vlaid 枚举标识符! - 优点:除了一堆
P99
header 之外,此实现是无库的。但是需要一些宏编程;此外,它符合 C99 标准; - 缺点:它符合 C99,但如果我们在 C11 上,我们可能会改进该方法,但是,OP 没有在标签中指定它,所以随便 :)。另一个缺点(我想您已经猜到了)是这个解决方案根本不使用该文件。更糟糕的是,它不能处理文件,因为从文件中提取的行不能用宏编程以任何方式处理。这就是我没有使用它的原因。我不知道您是否需要使用该文件或它足以以另一种方式列出您感兴趣的变量(在
TRACKED_FUNCTION
中使用此解决方案)。
希望对你有帮助
关于c - 在 C 中运行时打印变量的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56992975/