web-dev-qa-db-fra.com

Méthode programmatique pour obtenir le nom de la variable en C?

Je développe un outil pour vider les données des variables. Je dois vider le nom de la variable, ainsi que les valeurs.

Ma solution: stocke le nom de la variable sous forme de chaîne et affiche le "nom de la variable" suivi de sa valeur.

Existe-t-il un moyen programmatique de connaître le nom de la variable?

29
Alphaneo

Vous pouvez essayer quelque chose comme ça:

#define DUMP(varname) fprintf(stderr, "%s = %x", #varname, varname);

J'avais l'habitude d'utiliser cet en-tête j'ai écrit, quand j'étais nouveau sur C, il pouvait contenir des idées utiles. Par exemple, cela vous permettrait d’imprimer une valeur C et de fournir le spécificateur de format en un (ainsi que des informations supplémentaires):

#define TRACE(fmt, var) \
        (error_at_line(0, 0, __FILE__, __LINE__, "%s : " fmt, #var, var))

Si vous utilisez C++, vous pouvez utiliser le type de la valeur transmise et le générer correctement. Je peux donner un exemple beaucoup plus lucratif de la façon d’imprimer de jolies valeurs de variables si tel est le cas.

49
Matt Joiner

En C, les noms de variable existent lors de l’étape de compilation (et de l’étape de liaison, si la variable est globale), mais ne sont pas disponibles au moment de l’exécution. Vous devez choisir une solution impliquant une chaîne littérale indiquant le nom de la variable.

12
Greg Hewgill

J'ai en fait un code qui peut faire ce que vous voulez. Il utilise le préprocesseur pour définir le nom de la variable afin de vous permettre de l’imprimer. Il vide à la fois le nom et la valeur de la variable (en fonction du type) et la structure de la mémoire pour cette variable. Le programme suivant montre comment faire:

#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;
}

Cela génère:

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

Et, si vous vous interrogez sur la santé de do {...} while (0) dans les macros, c’est pour lui permettre de figurer n'importe où dans le code sans avoir à vous demander si vous avez suffisamment d’accolades autour.

6
paxdiablo

Chemin plus court:

#define GET_VARIABLE_NAME(Variable) (#Variable)

tester:

#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;
}
5
SaeidMo7

Si vous devez le faire pour des variables arbitraires, vous devrez probablement utiliser une API de débogage fournie par le compilateur ou la plate-forme (comme DbgHelp sous Windows).

Sur certains systèmes embarqués sur lesquels j'ai travaillé, nous devions pouvoir afficher sur commande les valeurs de certaines variables importantes connues à l'avance. Pour ce faire, nous avons simplement besoin d'un tableau nom/pointeur simple:

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


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

Ensuite, j'ai juste utilisé une petite routine qui cherche dans la table le nom de la variable en question et vide le nom et les données pointées par le pointeur. Si vous avez besoin de vider des données autres que des données entières, vous pouvez étendre la structure pour qu'elle contienne également un formateur de style printf, modifier le pointeur pour qu'il devienne un void* et transmettre ce courrier indésirable à snprintf() ou quelque chose pour formater les données.

Parfois, je vais aussi utiliser une macro qui aide à construire la table (éventuellement en déclarant la variable). Mais pour être honnête, je pense que cela complique vraiment la compréhension (en particulier pour les nouveaux venus dans le projet - ils ont souvent un petit moment "WTF?") Et ne simplifient pas vraiment les choses.

4
Michael Burr

Les gens veulent souvent que les programmes s'introspectent (ou "reflètent" dans le jargon actuel). Mais la plupart des langages de programmation offrent peu (par exemple, Java) ou aucune capacité (C) de refléter tous les détails d’un programme (noms de variables, noms de fonctions, types, structures d’expression, etc.).

Il existe un moyen de faire cela pour all languages: step outside la langue, et utiliser un outil conçu pour extraire cette information de la langue. Une classe d’outils capable de le faire est appelée système de transformation de programme.

Voir cette réponse SO pour une explication de la procédure à suivre pour "obtenir des noms de variables" et imprimer des valeurs à l'aide d'un système de transformation de programme: Suivi des modifications apportées aux variables

3
Ira Baxter

Essaye ça.

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

Code (void) La variable est une conversion void qui n’est pas opérationnelle alors il existe un opérateur virgule et une macro stringify. Le résultat net est donc const char * contenant le nom de la variable avec la vérification du compilateur si la variable existe réellement.

Vous avez maintenant le nom de votre variable et vous pouvez utiliser printf () pour en imprimer la valeur.

2
Timmy_A

Si votre exécutable est compilé avec des informations de débogage, vous pourrez peut-être obtenir ces informations. Sinon, vous n'avez probablement pas de chance. Donc, vous construisez un débogueur? Pourquoi? Les débogueurs existants pour c sont très matures. Pourquoi ne pas utiliser les outils existants au lieu de réinventer la roue?

1
Asaph

Je crains fort qu'il ne soit pas possible de faire cela dans votre programme (à part la réponse d'Anacrolix). Je pense que la bonne solution à votre problème est un script de débogage. Dans la plupart des débogueurs, vous pouvez même le connecter pour qu'il s'exécute chaque fois qu'il interrompt l'exécution du programme (point d'arrêt, vous frappez ^C, etc.) et obtenir un instantané de l'état de votre programme chaque fois que vous interagissez avec lui.

1
Carl Norum

Malgré la réponse très précise de @Matt Joiner , je veux écrire un code abrégé complet illustrant ceci, juste pour voir à quel point il est simple d'utiliser #define macro

#include <stdio.h>
char a;
#define dump(v)printf("%s",#v);
int main()
    {
    dump(a);
    }

sortie:

0
PYK