вывести значение переменных во время работы в C - PullRequest
1 голос
/ 11 июля 2019

Я пытаюсь протестировать некоторые пакеты, написанные на 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, y, d после оператора (x = x % 4) и (x = x + 1) Например:

5
7
6

фактический результат, который я получил, был:

x
y
d

1 Ответ

0 голосов
/ 12 июля 2019

Этот вопрос подразумевает использование отражения от локальных и глобальных переменных. К сожалению, у С нет такого понятия.

Я уверен, что GDB может помочь вам создать список локальных и глобальных переменных (возможно здесь ?)

Однако, поскольку вы прямо не сказали, как этого добиться, я брошу на это свои 2 цента.

Есть несколько проектов, реализующих рефлексию на C, но я всегда предпочитаю использовать препроцессор для достижения этой цели. Хотя вы можете избежать использования библиотеки только для заголовков, я буду использовать 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!

Я уже рассматривал подобную концепцию здесь , однако позвольте мне кратко объяснить, что делает этот код:

  • Использование : очень просто; после объявив все переменные, которые вы хотите печатать, вы вызываете 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 enum!
  • Плюсы : Эта реализация, за исключением нескольких заголовков P99, не содержит библиотек. Тем не менее, требуется некоторое макропрограммирование; Кроме того, он соответствует C99;
  • Минусы : он совместим с C99, но мы, вероятно, можем улучшить метод, если бы мы работали на C11, однако OP не указывал его в тегах, что угодно :). Еще одним минусом (я думаю, вы уже догадались) является тот факт, что это решение вообще не использует файл. Хуже того, он не может работать с файлом, потому что строки, извлеченные из файла, никак не могут быть обработаны с помощью макропрограммирования. Вот почему я не использовал его. Я не знаю, требуется ли вам использовать файл или достаточно перечислить другой способ (с этим решением в TRACKED_FUNCTION) интересующие вас переменные.

Надеюсь, это поможет

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