2015-05-20 11 views
12

Voglio controllare il formato di uscita printf() funzioni con parametro dinamico, proprio come il codice mostrato qui sotto:Perché non posso impostare il formato di output di printf con argomenti dinamici?

#include<stdio.h> 

    int main(int argc,char ** argv) 
    { 
     printf(argv[1],"hello,world"); 
     return 0; 
    } 

Poi posso compilare ed eseguirlo:

$ gcc -o test test.c 
$ ./test "\t%s\n" 

Il risultato è strano:

\thello,world\n$ 

Perché "\n" e "\t" non ha alcun effetto?

+6

Beh chiaramente possibile impostare il formato di printf con quello arg, avete ancora il vostro \ t \ n. Il problema è che questi personaggi non sembrano essere letti come tabulazione e avanzamento di riga. Inoltre, ** non chiamare la stampa con un primo arg da input dell'utente è un difetto di sicurezza huuuuuge !!! ** – Eregrith

+6

vedi [format string vulnerability] (http://www.cis.syr.edu/~wedu/Teaching/ cis643/LectureNotes_New/Format_String.pdf) ad esempio :) – Eregrith

+1

Questa domanda (o la sua risposta) in realtà non riguarda C o 'printf', ma sul perché la shell non interpreta' \ t' e '\ n' all'interno di stringhe in allo stesso modo del compilatore C. –

risposta

9

Poiché gli escape utilizzati (\t e \n) sono interpretati all'interno di stringhe letterali dal compilatore C, non da printf(). Questo:

const char *newline1 = "\n", newline2[] = { '\n', 0 }; 

genererebbe esattamente lo stesso contenuto di newline1 e newline2, a prescindere dal fatto che questi sono mai passati al printf(); le corde ci sono comunque.

Il codice si comporta proprio come questo sarebbe:

printf("\\t%s\\n", "hello,world"); 

qui, ho doppio escape i caratteri speciali per generare una stringa con lo stesso contenuto effettivo come argomento della riga di comando, cioè "\t%s\n" (sei personaggi anziché quattro).

Il modo corretto per controllare dinamicamente printf() consiste nel creare la stringa di formato nel codice. Se si desidera escapes di tipo C in fase di esecuzione, è necessario interpretarli in qualche modo.

+1

Se si desidera che la shell interpreti sequenze di escape speciali, si potrebbe usare un altro comando tra le backquote per interpretare prima l'escape e passarlo al programma come stringa. – Eregrith

4

È perché il compilatore gestisce le sequenze di escape come "\n" ecc. E lo fa solo in stringhe o caratteri letterali.

6

La sequenza \n in una stringa o carattere letterale in C/C++ è un singolo byte con il valore numerico 10 (su un sistema ASCII). Quando si stampa su un terminale (prova putchar(10)!) Imposta semplicemente la posizione di uscita per il carattere successivo sul terminale all'inizio della riga successiva (su * nix; su MacOS, penso, è necessario un ulteriore \r o 13 per ritorno a capo per avere la posizione di uscita all'inizio della riga).

Analogamente, uno \t è la notazione per un singolo byte con il valore 9, che fa sì che la maggior parte dei terminali faccia avanzare il cursore alla posizione successiva del tabulatore.

Quello che serve è inserire un singolo byte di questi valori nella riga di comando. Come ciò può essere fatto dipende dalla tua shell; in bash puoi impedire alla shell di interpretare caratteri speciali premendo Ctrl-V in anticipo. Questo produce, ad es. una scheda, visualizzata mostrando uno spazio vuoto (invece di fare in modo che la shell mostri possibili continuazioni di stringa o qualsiasi altra cosa faccia in bash). le stringhe di bash in virgolette singole o doppie possono includere nuove righe senza ulteriori sforzi: è sufficiente premere Invio.

Ecco un esempio di esecuzione in un terminale cygwin con bash. Ho premuto i tasti indicati nelle posizioni indicate; Ho finito il comando come al solito con [ritorno] dopo la citazione singola di chiusura sulla seconda riga.

pressed Ctrl-v,[TAB] here | pressed [return] there 
        v  v 
$ ./printf-arg.exe ' %s 
> ' 
     hello,world 

Il > nella seconda linea era uscita dalla shell dopo aver premuto inserite all'interno della stringa delimitata da apici. (Che inserisce una nuova riga nella stringa). È un'indicazione che la stringa che si sta modificando viene proseguita su quella linea.

Per inciso, probabilmente non è sicuro utilizzare gli argomenti della riga di comando in questo modo in ambienti potenzialmente ostili. Stringhe attentamente elaborate potrebbero accedere alla memoria a cui non si intende accedere e ad es. reindirizzare gli indirizzi di ritorno, corrompendo il programma.

3

se si passa interpretato "\ t% s \ n" al comando funzionerà. Tuttavia è complicato costruire tale stringa nella shell. Il modo più semplice che conosco è:

./test $'\t%s\n' 

Vedere ANSI citando in man bash per il $'magick'