2011-11-10 4 views
6

Voglio scrivere una macro per scrivere una stringa, utilizzando l'ottimizzazione in fase di compilazione della conoscenza della lunghezza di una stringa letterale. Ma ho bisogno di rilevare l'uso improprio, usando i puntatori.Come distinguere la stringa costante da char * nella macro C

Ecco cosa intendo:

void transmit(const char *data, int length); 
#define tx_string(x) transmit((x), sizeof(x) -1) 
void func(char *badmsg) { 
    tx_string("GO"); // ok 
    tx_string(badmsg); // not OK 
} 

Con la seconda chiamata, la dimensione sarà una sciocchezza (sizeof un puntatore).

Voglio produrre un errore in fase di compilazione se tento di utilizzare tx_string su qualcosa di diverso da una stringa letterale. Questo sta usando gcc; c'è qualche cosa gcc che posso usare per fare questo?

Modifica: lavoro con buffer di dati che possono contenere valori nulli o non essere terminati da null. VERO VERAMENTE che si impedisca di utilizzare i puntatori a questo scopo e di impedire l'utilizzo di runtime strlen().

Edit 2:

Ecco un esempio che causerebbe un problema. Inventerò un protocollo immaginario in cui devo dire a un microcontrollore a 16 bit un indirizzo usando il comando GO seguito da un indirizzo come 16-bit raw (due caratteri a 8 bit), e voglio passare dall'indirizzo 0.

#define GOSEQ "GO\000\000" 

void func(void) { 
    char *bad = GOSEQ; 
    tx_string(GOSEQ); // ok, sends 4 bytes: GO and two zero bytes 
    tx_string(bad); // bad, using runtime strlen sends two characters "GO" 
} 

Sono sicuro che ci deve essere una sorta di controllo integrato gcc per questo. Vedo i sorgenti del kernel di Linux usando trucchi di distinzione in fase di compilazione come questo molto .. ma non posso mettere le mani su uno specifico rapidamente.

Finora l'idea di "programmatore di Windows" sembra buona, ma un errore di compilazione più significativo sarebbe un vantaggio.

risposta

9

In generale, dal momento che non è possibile utilizzare la concatenazione di stringhe con i puntatori, ecc, forse è possibile utilizzare:

#define STRLIT(x) x "" 

Se l'argomento per STRLIT non è una stringa letterale, si otterrà un errore di compilazione.

Adattare il generale allo specifico macro:

#define tx_string(x) transmit((x ""), sizeof(x) - 1) 
+0

Questo sembra un vincitore! Aspetterò un attimo nel caso qualcuno faccia il test perfetto o qualcosa di simile. – blueshift

4

Si può fare questo:

#define tx_string(x) transmit(x, strlen(x)) // No need for extra parentheses 

GCC ottimizzerà la chiamata strlen, e sono sicuro che altri compilatori sarà troppo. Non perdere tempo con queste cose.

file di test:

#include <string.h> 
void transmit(const char *, size_t); 
#define tx_string(x) transmit(x, strlen(x)) 
void func(void) 
{ 
    tx_string("abcdef"); 
} 

risultante di montaggio:

ho tagliato rumore fuori del gruppo, questa è la roba importante. Si noti che non chiama mai strlen, invece utilizza solo il numero 6:

.LC0: 
    .string "abcdef" 

func: 
    movl $6, %esi 
    movl $.LC0, %edi 
    jmp  transmit 
+0

OK, ma io lavoro con i buffer di dati che possono contenere valori nulli o non essere terminato da null. Voglio VERAMENTE che i puntatori vengano utilizzati per questo scopo e impediscano l'utilizzo di strlen() in fase di esecuzione. – blueshift

+1

@blueshift: In C, i puntatori e gli array sono intercambiabili come argomenti per le funzioni. Perché è necessario impedire che vengano passati i puntatori? Non ha senso per me. Sono anche incerto sul motivo per cui è necessario evitare l'uso 'strlen' del runtime. Potresti spiegarlo? –

+0

@Deitrich Epp: Il mio commento precedente dovrebbe spiegarlo. Modificherò la domanda per renderla più chiara. – blueshift

1
#define tx_string(x) ("i came, i concatenated, i discarded " x, transmit((x), sizeof(x) - 1)) 

Non del tutto perfetto, perché qualche buontempone potrebbe chiamare tx_string (+1)

+0

Vedo cosa hai fatto lì. Subdolo. Sono sorpreso dal fatto che sizeof (+1) compili. In realtà se la stringa degli scarti viene spostata a destra di x, sembra abbastanza buona ... non si può pensare immediatamente a qualcosa di non valido che compila. – blueshift

+0

Hai ragione, la risposta di Jonathan Leffler risolve il mio piccolo difetto. Nel frattempo, non dovrebbe sorprendere che la dimensione di alcune compilazioni int e il suo valore siano le dimensioni di un int. –