Modo programmatico per ottenere il nome della variabile in C?

Sto sviluppando uno strumento per scaricare dati da variabili. Devo scaricare il nome della variabile e anche i valori.

La mia soluzione: memorizza il nome della variabile come una stringa e stampa il “nome della variabile”, seguito dal suo valore.

C’è un modo programmatico per conoscere il nome della variabile?

Potresti provare qualcosa come questo:

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

Usavo questa intestazione che ho scritto, quando ero nuovo in C, poteva contenere alcune idee utili. Ad esempio ciò consentirebbe di stampare un valore C e fornire lo specificatore di formato in uno (nonché alcune informazioni aggiuntive):

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

Se stai usando C ++, puoi usare il tipo del valore passato e emetterlo in modo appropriato. Posso fornire un esempio molto più lucrativo su come “stampare bene” i valori delle variabili se questo è il caso.

In C, i nomi delle variabili esistono durante la fase di compilazione (e la fase di collegamento, se la variabile è globale), ma non sono disponibili in fase di esecuzione. È necessario scegliere una soluzione che prevede una stringa letterale che indica il nome della variabile.

In realtà ho un codice che può fare ciò che vuoi. Usa il preprocessore per stringere il nome della variabile per permetterti di stamparlo. Scarica sia il nome e il valore della variabile (in base al tipo) sia il layout della memoria per quella variabile. Il seguente programma mostra come è fatto:

 #include  #include  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; zc = 'a'; zi = 42; strcpy (z.c2,"Hello"); DUMPINT (i); DUMPSTR (s); DUMPMEM (z,sizeof(z)); return 0; } 

Questo produce:

 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 

E, se ti stai interrogando sulla sanità mentale di do {...} while (0) nei macro, è necessario abilitarla per essere inserita in qualsiasi parte del codice senza doversi preoccupare se hai abbastanza parentesi.

Se è necessario eseguire questa operazione per variabili arbitrarie, sarà probabilmente necessario utilizzare un’API debugger fornita dal compilatore o dalla piattaforma (come DbgHelp su Windows).

Su alcuni sistemi embedded su cui ho lavorato, dovevamo essere in grado di visualizzare a comando i valori di alcune importanti variabili conosciute in anticipo, e per fare ciò tutto ciò di cui abbiamo bisogno è una semplice tabella Name / pointer:

 typedef struct vartab { char const* name; int * var; } vartab; vartab varTable[] = { { "foo", &foo }, { "bar", &bar } }; 

Quindi ho appena usato una piccola routine che cerca nella tabella il nome della variabile in questione e scarica il nome e i dati puntati dal puntatore. Se è necessario eseguire il dump di dati diversi da quelli semplici, è ansible estendere la struttura per contenere anche un formattatore di tipo printf e modificare il puntatore come void* e passare tale posta indesiderata a snprintf() o qualcosa per formattare i dati.

A volte userò anche una macro che aiuta a build la tabella (probabilmente anche dichiarando la variabile). Ma per essere onesti, penso che ciò rende davvero più complicata la comprensione (specialmente per qualcuno che si unisce al progetto – spesso hanno un piccolo momento “WTF?”) E in realtà non semplifica molto le cose.

Più breve:

 #define GET_VARIABLE_NAME(Variable) (#Variable) 

test:

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

Le persone spesso vogliono programmi per auto-introspezione (o “riflettono” nel gergo corrente). Ma la maggior parte dei linguaggi di programmazione offre poche (ad esempio, Java) o nessuna (C) capacità di riflettere su tutti i dettagli di un programma (nomi di variabili, nomi di funzioni, tipi, strutture di espressione, ecc.).

C’è un modo per farlo in tutte le lingue: esci dalla lingua e usa uno strumento progettato per estrarre le informazioni dalla lingua. Una class di strumenti che può fare ciò è chiamata sistema di trasformazione del programma.

Vedi questa risposta SO per una discussione su come “ottenere nomi di variabili” e stampare valori usando un sistema di trasformazione del programma: traccia automaticamente le modifiche alle variabili

Prova questo.

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

La variabile Code (void) è la conversione void che è no-op, quindi c’è l’operatore virgola e la stringify della macro. Quindi il risultato netto è const char * contenente il nome della variabile con il controllo del compilatore la variabile esiste realmente.

Ora hai il nome della tua variabile e puoi usare printf () per stampare il suo valore.

Se il tuo eseguibile è compilato con informazioni di debug, potresti essere in grado di ottenere queste informazioni. In caso contrario, probabilmente sei sfortunato. Quindi stai costruendo un debugger? Perché? I debugger esistenti per c sono molto maturi. Perché non utilizzare gli strumenti esistenti invece di reinventare la ruota?

Non c’è un buon modo per farlo dall’interno del tuo programma, temo (a parte la risposta di Anacrolix). Penso che la soluzione giusta al tuo problema sia uno script di debugger. Nella maggior parte dei debugger, puoi persino collegarlo per eseguire ogni volta che il debugger interrompe l’esecuzione del programma (punto di interruzione, hai premuto ^C , ecc.) E ottieni un’istantanea dello stato del tuo programma ogni volta che interagisci con esso.