在C中运行时打印变量的值

在C中运行时打印变量的值,c,dictionary,variables,testing,updating,C,Dictionary,Variables,Testing,Updating,我试图通过检查局部变量和全局变量的值来测试一些用C编写的包,我尝试使用GDB调试器和fprint,正如这里许多人所建议的那样,它们可以很好地处理简单和小的程序,但是使用包并不容易 因此,我需要将所有变量提取到一个txt.file(每行有一个变量)中,然后在运行程序时,我需要打印这些变量的值 我使用了一个普通的print语句从txt文件中获取变量的名称,问题是打印的字符是否准确 问题:如何使用文本文件中的这些字符作为变量来打印值而不是名称 variables.txt x y d main.c 在

我试图通过检查局部变量和全局变量的值来测试一些用C编写的包,我尝试使用GDB调试器和fprint,正如这里许多人所建议的那样,它们可以很好地处理简单和小的程序,但是使用包并不容易

因此,我需要将所有变量提取到一个txt.file(每行有一个变量)中,然后在运行程序时,我需要打印这些变量的值

我使用了一个普通的print语句从txt文件中获取变量的名称,问题是打印的字符是否准确

问题:如何使用文本文件中的这些字符作为变量来打印值而不是名称

variables.txt

x
y
d
main.c

在主文件中,我包含了头并调用了func

//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)
例如:

5
7
6
我得到的实际产出是:

x
y
d

这个问题意味着对局部变量和全局变量使用反射。遗憾的是,C没有这样的概念

我相信gdb可以帮助您生成本地和全局变量列表(也许?)

不过,既然你没有明确说明如何实现这一点,我就花2美分在这上面

有一些项目在C上实现反射,但我更喜欢使用预处理器来实现这一点。虽然您可以避免使用仅标头库,但我将使用它来简化宏编程

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!");
}
我已经处理了一个类似的概念,但是让我简单地解释一下这段代码的作用:

  • 用法:非常简单声明所有要打印的变量后,调用
    TRACKED_函数
    并传递一系列变量对:变量对的第一个元素是一个字符串,表示希望如何打印变量的内容(例如,您可能有一个
    char
    ,但可能希望将其打印为
    int
    );第二个是变量本身的名称;因此在示例
    TRACKED_函数中(int,x,int,y,ptr,yPtr,string,name)
    我们希望跟踪需要打印为
    int
    x
    y
    作为
    int
    yPtr
    作为指针和
    name
    作为字符串;调用这样的宏后,只要您想获得明确声明的所有变量的列表,就可以调用
    printState()
  • 内部细节:我的主要想法是创建4个“隐藏”变量:一个数组包含所有变量名称,另一个数组包含所有变量指针,另一个数组包含每个变量如何打印,以及一个表示所有数组长度的整数;
    TRACKED\u FUNCTION
    printState()时生成这3个数组
    只需打印它们。特别是,
    TRACKED\u函数
    中每对的第一个元素实际上连接到一个标识符中,该标识符属于enum
    print\u type
    :这是我们可以使用“type”的主要原因
    string
    但我们不能使用类型
    void*
    PT\u void*
    不是vlaid枚举标识符
  • Pros:除了一堆
    P99
    的头之外,这个实现是无库的。不过需要一些宏编程;而且它符合C99
  • Cons:它符合C99标准,但是如果我们使用C11,我们可能会改进该方法,但是OP没有在标记中指定它,所以不管怎样:)。另一个缺点(我想你已经猜到了)是这个解决方案根本不使用文件。更糟糕的是,它无法处理文件,因为宏编程无法以任何方式处理从文件中获取的行。这就是为什么我没有使用它。我不知道您是否需要使用该文件或它以另一种方式列出您感兴趣的变量(使用
    TRACKED\u FUNCTION
    中的此解决方案)

希望它能有所帮助

这个问题暗示了对局部和全局变量的反思。遗憾的是,C没有这样的概念

我相信gdb可以帮助您生成本地和全局变量列表(也许?)

不过,既然你没有明确说明如何实现这一点,我就花2美分在这上面

有一些项目在C上实现反射,但我更喜欢使用预处理器来实现这一点。虽然您可以避免使用仅标头库,但我将使用它来简化宏编程

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!");
}
我已经处理了一个类似的概念,但是让我简单地解释一下这段代码的作用:

  • 用法:非常简单声明所有要打印的变量后,调用
    TRACKED_函数
    并传递一系列变量对:变量对的第一个元素是一个字符串,表示希望如何打印变量的内容(例如,您可能有一个
    char
    ,但可能希望将其打印为
    int
    );第二个是变量本身的名称;因此在示例
    TRACKED_函数中(int,x,int,y,ptr,yPtr,string,name)
    我们希望跟踪需要打印为
    int
    x
    y
    作为
    int
    yPtr
    作为指针和
    name
    作为字符串;调用这样的宏后,只要您想获得明确声明的所有变量的列表,就可以调用
    printState()
  • 内部详细信息:我的主要想法是创建4个“隐藏”变量:一个包含所有变量名称的数组,另一个包含所有变量指针,另一个包含每个变量如何打印的数组和一个整数repr