C ++ printf con std :: string?

La mia comprensione è che la string è un membro dello spazio dei nomi std , quindi perché si verifica quanto segue?

 #include  int main() { using namespace std; string myString = "Press ENTER to quit program!"; cout << "Come up and C++ me some time." << endl; printf("Follow this command: %s", myString); cin.get(); return 0; } 

inserisci la descrizione dell'immagine qui

Ogni volta che si esegue il programma, myString stampa una stringa apparentemente casuale di 3 caratteri, ad esempio nell’output precedente.

Si sta compilando perché printf non è sicuro, poiché usa gli argomenti variabili nel C senso 1 . printf non ha alcuna opzione per std::string , solo una stringa in stile C. Utilizzare qualcos’altro al posto di ciò che si aspetta sicuramente non ti darà i risultati che desideri. In realtà è un comportamento indefinito, quindi potrebbe succedere di tutto.

Il modo più semplice per risolvere questo problema, dato che stai usando C ++, è la stampa normalmente con std::cout , dal momento che std::string supporta l’overloading dell’operatore:

 std::cout << "Follow this command: " << myString; 

Se, per qualche ragione, hai bisogno di estrarre la stringa in stile C, puoi usare il metodo c_str() di std::string per ottenere un const char * che è terminato con null. Usando il tuo esempio:

 #include  #include  #include  int main() { using namespace std; string myString = "Press ENTER to quit program!"; cout << "Come up and C++ me some time." << endl; printf("Follow this command: %s", myString.c_str()); //note the use of c_str cin.get(); return 0; } 

Se si desidera una funzione simile a printf , ma di tipo sicuro, esaminare i modelli variadic (C ++ 11, supportato su tutti i principali compilatori a partire da MSVC12). Puoi trovare un esempio di questo qui . Non c'è nulla che io sappia implementato in questo modo nella libreria standard, ma potrebbe esserci in Boost, in particolare boost::format .


[1]: Ciò significa che è ansible passare qualsiasi numero di argomenti, ma la funzione si basa su di te per dirgli il numero e i tipi di tali argomenti. Nel caso di printf , ciò significa una stringa con informazioni di tipo codificato come %d significa int . Se menti sul tipo o sul numero, la funzione non ha un metodo standard di conoscenza, sebbene alcuni compilatori abbiano la possibilità di controllare e dare avvertimenti quando menti.

Si prega di non utilizzare printf("%s", your_string.c_str());

Usa cout << your_string; anziché. Breve, semplice e typescript. In effetti, quando scrivi C ++, in genere vuoi evitare completamente printf - è un residuo di C che è raramente necessario o utile in C ++.

Per quanto riguarda il motivo per cui dovresti usare il cout invece di printf , i motivi sono numerosi. Ecco alcuni esempi tra i più ovvi:

  1. Come mostra la domanda, printf non è sicuro per il tipo. Se il tipo che passi è diverso da quello indicato nello specificatore di conversione, printf proverà ad usare qualsiasi cosa trovi nello stack come se fosse il tipo specificato, dando un comportamento indefinito. Alcuni compilatori possono mettere in guardia su questo in alcune circostanze, ma alcuni compilatori non possono / non lo faranno affatto e nessuno può in tutte le circostanze.
  2. printf non è estensibile. Puoi solo passare tipi primitivi ad esso. Il set di specificatori di conversione che comprende è hard-coded nella sua implementazione e non è ansible aggiungere altri / altri. Il C ++ più ben scritto dovrebbe usare questi tipi principalmente per implementare tipi orientati al problema da risolvere.
  3. Rende la formattazione decente molto più difficile. Per un esempio ovvio, quando si stampano numeri da leggere per le persone, in genere si desidera inserire migliaia di separatori con poche cifre. Il numero esatto di cifre e i caratteri utilizzati come separatori varia, ma cout ha anche quello coperto. Per esempio:

     std::locale loc(""); std::cout.imbue(loc); std::cout << 123456.78; 

    L'impostazione locale senza nome ("") seleziona una locale in base alla configurazione dell'utente. Pertanto, sulla mia macchina (configurata per l'inglese americano) viene 123,456.78 come 123,456.78 . Per qualcuno che ha il proprio computer configurato per (diciamo) la Germania, dovrebbe stampare qualcosa come 123.456,78 . Per qualcuno con esso configurato per l'India, sarebbe stampato come 1,23,456.78 (e naturalmente ce ne sono molti altri). Con printf ottengo esattamente un risultato: 123456.78 . È coerente, ma è sempre sbagliato per tutti ovunque. Essenzialmente l'unico modo per aggirare il problema è di fare la formattazione separatamente, quindi passare il risultato come stringa a printf , perché printf stesso semplicemente non farà il lavoro correttamente.

  4. Sebbene siano abbastanza compatte, le stringhe in formato printf possono essere abbastanza illeggibili. Anche tra i programmatori C che usano praticamente printf ogni giorno, direi che almeno il 99% dovrebbe cercare le cose per essere sicuri di cosa significhi il # in %#x , e come ciò differisca da ciò che il # in %#f significa (e sì, significano cose completamente diverse).

usa myString.c_str () se vuoi una stringa c-like (const char *) da usare con printf

Utilizzare l’esempio std :: printf e c_str ():

 std::printf("Follow this command: %s", myString.c_str()); 

Printf è in realtà piuttosto buono da usare se le dimensioni contano. Significato se si sta eseguendo un programma in cui la memoria è un problema, quindi printf è in realtà una soluzione molto buona e sotto rater. Cout essenzialmente sposta i bit sopra per fare spazio alla stringa, mentre printf prende solo una sorta di parametri e la stampa sullo schermo. Se dovessi compilare un semplice programma di ciao mondo, printf sarebbe in grado di compilarlo in meno di 60.000 bit anziché in cout, ci vorrebbe più di 1 milione di bit da compilare.

Per la tua situazione, id suggerisce di usare cout semplicemente perché è molto più comodo da usare. Anche se, direi che printf è qualcosa di buono da sapere.

Il motivo principale è probabilmente che una stringa C ++ è una struttura che include un valore di lunghezza corrente, non solo l’indirizzo di una sequenza di caratteri terminata da un byte 0. Printf ei suoi parenti si aspettano di trovare una tale sequenza, non una struttura, e quindi essere confusi dalle stringhe C ++.

Per quanto mi riguarda, credo che printf abbia un posto che non può essere facilmente riempito dalle caratteristiche sintattiche del C ++, proprio come le strutture di tabelle in html hanno un posto che non può essere facilmente riempito dai div. Come Dykstra ha scritto in seguito sul goto, non intendeva iniziare una religione e stava solo discutendo contro l’uso di un kludge per compensare un codice mal progettato.

Sarebbe molto carino se il progetto GNU aggiungesse la famiglia printf alle loro estensioni g ++.

printf accetta un numero variabile di argomenti. Quelli possono avere solo tipi Plain Old Data (POD). Il codice che trasmette qualsiasi cosa diversa da POD per printf solo compila perché il compilatore presuppone che hai ottenuto il tuo formato giusto. %s significa che il rispettivo argomento dovrebbe essere un puntatore a un char . Nel tuo caso è uno std::string not const char* . printf non lo sa perché il tipo di argomento va perso e dovrebbe essere ripristinato dal parametro format. Quando si trasforma l’argomento std::string in const char* il puntatore risultante punterà a un’area di memoria irrilevante invece della stringa C desiderata. Per questo motivo il tuo codice stampa senza senso.

Mentre printf è una scelta eccellente per la stampa di testo formattato , (specialmente se si intende avere padding), può essere pericoloso se non si sono abilitati gli avvisi del compilatore. Abilita sempre gli avvisi perché errori di questo tipo sono facilmente evitabili. Non c’è motivo di usare il goffo meccanismo std::cout se la famiglia printf può svolgere lo stesso compito in un modo molto più rapido e carino. Assicurati di aver triggersto tutti gli avvisi ( -Wall -Wextra ) e sarai bravo. Nel caso in cui si usi la propria implementazione printf personalizzata, è necessario dichiararla con il meccanismo __attribute__ che consente al compilatore di verificare la stringa di formato rispetto ai parametri forniti .