C ++ printf cu std :: string?

Înțelegerea mea este că string este un membru al spațiului de nume std , deci de ce apar următoarele?

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

introduceți descrierea imaginii aici

De fiecare dată când rulează programul, myString imprimă un șir aparent aleator de 3 caractere, cum ar fi în ieșirea de mai sus.

0
ouf! bine, este bine să țineți cont de acest lucru în timp ce mă străduiesc prin carte. Sunt sigur că nu va fi singura carte C ++ pe care o voi citi pe parcursul anului următor sau așa, așa că sper că nu face prea mult damange :)
adăugat autor TheDarkIn1978, sursa
Utilizarea celui mai înalt avertisment de compilator ar răspunde la întrebarea dvs. - atunci când compilarea cu gcc. Cum gestionează MSVC acest lucru - nu știu.
adăugat autor Al Bundy, sursa
Doar pentru a vă spune, o mulțime de oameni critica acea carte. Ceea ce pot înțelege, pentru că nu există multe despre programarea orientată pe obiecte, dar nu cred că este la fel de rău ca și oamenii.
adăugat autor Jesse Good, sursa

7 răspunsuri

Se compilează deoarece printf nu este tip sigur, deoarece utilizează argumente variabile în sens C 1 . printf nu are opțiune pentru std :: string , ci doar un șir de stil C. Folosind altceva în locul a ceea ce se așteaptă cu siguranță nu vă va oferi rezultatele dorite. Este de fapt un comportament nedefinit, deci orice ar putea să se întâmple.

Cel mai simplu mod de a rezolva acest lucru, deoarece utilizați C ++, îl tipărește în mod normal cu std :: cout , deoarece std :: string susține că prin supraîncărcarea operatorului:

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

Dacă, din anumite motive, trebuie să extrageți șirul de stil C, puteți folosi metoda c_str() din std :: string pentru a obține un const char * care este null-terminat. Folosind exemplul dvs.:

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

Dacă doriți o funcție care să fie ca și printf , dar tip sigură, căutați în șabloanele variadic (C ++ 11, acceptate pe toate compilatoarele importante ca și MSVC12). Puteți găsi un exemplu de aici . Nu există nimic despre care să știu că a fost pus în aplicare astfel în biblioteca standard, dar ar putea fi în Boost, în special impuls :: format .


[1]: Aceasta înseamnă că poți transmite orice număr de argumente, dar funcția se bazează pe tine să-i spui numărul și tipurile acestor argumente. În cazul printf , înseamnă un șir cu informații de tip codificat, cum ar fi % d , care înseamnă int . Dacă mințiți despre tipul sau numărul, funcția nu are un mod standard de cunoaștere, deși unii compilatori au capacitatea de a verifica și de a da avertismente atunci când minți.

0
adăugat
@ MoyingDuck, punct bun. Este în răspunsul lui Jerry, dar fiind răspunsul acceptat, acesta este ceea ce văd oamenii și ar putea să plece înainte să vadă pe ceilalți. Am adăugat această opțiune pentru a fi prima soluție văzută și una recomandată.
adăugat autor chris, sursa
nici o mențiune despre utilizarea cout pentru șir?
adăugat autor Mooing Duck, sursa

Please don't use printf("%s", your_string.c_str());

Use cout << your_string; instead. Short, simple and typesafe. In fact, when you're writing C++, you generally want to avoid printf entirely -- it's a leftover from C that's rarely needed or useful in C++.

În ceea ce privește de ce ar trebui să utilizați cout în loc de printf , motivele sunt numeroase. Iată o mostră de câteva dintre cele mai evidente:

  1. As the question shows, printf isn't type-safe. If the type you pass differs from that given in the conversion specifier, printf will try to use whatever it finds on the stack as if it were the specified type, giving undefined behavior. Some compilers can warn about this under some circumstances, but some compilers can't/won't at all, and none can under all circumstances.
  2. printf isn't extensible. You can only pass primitive types to it. The set of conversion specifiers it understands is hard-coded in its implementation, and there's no way for you to add more/others. Most well-written C++ should use these types primarily to implement types oriented toward the problem being solved.
  3. It makes decent formatting much more difficult. For an obvious example, when you're printing numbers for people to read, you typically want to insert thousands separators every few digits. The exact number of digits and the characters used as separators varies, but cout has that covered as well. For example:

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

    The nameless locale (the "") picks a locale based on the user's configuration. Therefore, on my machine (configured for US English) this prints out as 123,456.78. For somebody who has their computer configured for (say) Germany, it would print out something like 123.456,78. For somebody with it configured for India, it would print out as 1,23,456.78 (and of course there are many others). With printf I get exactly one result: 123456.78. It is consistent, but it's consistently wrong for everybody everywhere. Essentially the only way to work around it is to do the formatting separately, then pass the result as a string to printf, because printf itself simply will not do the job correctly.

  4. Although they're quite compact, printf format strings can be quite unreadable. Even among C programmers who use printf virtually every day, I'd guess at least 99% would need to look things up to be sure what the # in %#x means, and how that differs from what the # in %#f means (and yes, they mean entirely different things).
0
adăugat
atunci cand incerc sa compilati cout << myString << endl; primesc urmatoarea eroare: Eroare 1 eroare C2679: binary << <<: nu a fost gasit niciun operator care are un operand de dreapta de tip "std :: string" (sau nu există o conversie acceptabilă)
adăugat autor TheDarkIn1978, sursa
că am uitat (de fapt, nu știu despre)! Mulțumiri :)
adăugat autor TheDarkIn1978, sursa
@Jerry: Vreau doar să subliniez faptul că folosind printf este mult mai rapid decât utilizarea cout atunci când se ocupă cu date mari. Astfel, vă rog să nu spuneți că este inutil: D
adăugat autor Programmer, sursa
printf este un thread-safe (cout nu este) și o mulțime de formatări are o performanță mai bună folosind printf decât utilizarea fluxurilor (cout, stringstream, etc.).
adăugat autor ebasconp, sursa
@JerryCoffin Cineva sa confruntat cu probleme în a scrie acest lucru: msdn.microsoft.com/en -us/magazine/dn913181.aspx În mod clar are fanii săi :-)
adăugat autor ForeverLearning, sursa
Verificarea tipului funcționează numai dacă șirul de format este un literal șir Da, și cu asta mă ocup. Situație diferită -> altă soluție. gcc mă avertizează deja în timpul compilației. Când mă gândesc doar la soluția cu cout și metodele rudimentare mă îmbolnăvesc.
adăugat autor Al Bundy, sursa
Nu, atunci când lucrați cu gcc cu toate comutatoarele de avertizare activate. Nu este nimic mai sigur și mai ales mai convenabil: 100% tipafe, verificarea numărului de argumente. Formatarea cu cout este un eveniment de groază - mai ales pentru intrările de fișiere log lungi. Întotdeauna râd când C ++ guru-ul vine cu cout << "Hallo" sau un număr. Dar fă-o cu 30 de argumente [așa cum am vorbit pentru un jurnal] ...
adăugat autor Al Bundy, sursa
@kuroineko Sunt de acord cu 100% în legătură cu nota dvs. Tipic ... În ochii mei cout este cea mai gravă caracteristică C ++.
adăugat autor Al Bundy, sursa
Care este partea contra printf în c ++ când acești parametri intră în acțiune? (am uitat numele, vreau sa zic acele cateva litere!)
adăugat autor Breeze, sursa
@ AlBundy: Nu, nu este chiar aproape de 100% de tip sigur. Verificarea tipului funcționează numai atunci când șirul de format este un literal șir. Dacă chiar încerci să faci meseria profesională, asta nu se întâmplă niciodată - șirurile de format sunt întotdeauna încărcate dintr-un fișier extern pentru a permite localizarea. În acest proces, pierdeți verificarea de tip toate .
adăugat autor Jerry Coffin, sursa
@AlBundy: Sunt de acord ca cout (bine, iostreams in general) au o multime de probleme. Chiar și așa, ele sunt mai bune decât printf într-o mulțime de moduri.
adăugat autor Jerry Coffin, sursa
@Dilip: Nu - și într-un fel, sunt chiar printre ei. Cu siguranta nu-mi plac verbositatea iostreams atunci cand trebuie sa formatezi output-ul exact (la doar un exemplu evident). Cu toate acestea, daca printf este raspunsul, cineva a intrebat o intrebare proasta.
adăugat autor Jerry Coffin, sursa
@ AlBundy: Aș sugera să vă adresați o întrebare despre cum să faceți ceea ce încercați să faceți. Presupun că se pune problema unei întrebări cu privire la ceea ce ești dispus să suporți - așa cum am spus deja (și nu glumea) cout suge. Pe de altă parte, lipsa completă a tipului de verificare de tip printf merge mai degrabă în urma sugerii și a devenit inacceptabilă, care se învecinează cu criminalul.
adăugat autor Jerry Coffin, sursa
@LouisStrous: La un moment dat aș fi fost de acord (și cu ani în urmă au postat declarații similare). Acest lucru vă permite să faceți ceva, dar faceți-o atât de prost că sunteți cu adevărat mai bine să nu fi făcut-o deloc, ca nu cumva cineva sa o greseasca pentru un port finalizat/utilizabil. Aceasta duce la ceea ce se consideră a fi "sindromul MS-DOS" - atât de sărac, încât este aproape inutil, dar suficient pentru a convinge mulți că este suficient de bun, așa că nu încearcă să-l îmbunătățească.
adăugat autor Jerry Coffin, sursa
@oopscene: POSIX necesită printf pentru a fi thread-safe, dar nu toate sistemele sunt complet conforme cu POSIX (unele, desigur, nici măcar nu încercați). Nu este suficient de clar ce vrei să spui prin faptul că "se comportă mai bine" să comenteze în mod semnificativ acest lucru. În cele din urmă, se ajunge la un singur lucru: da, există cel puțin cazuri în care există cel puțin un argument rezonabil care trebuie făcut în favoarea utilizării printf - dar astfel de cazuri sunt mult mai puțin obișnuită decât unii oameni cred că și cazul la îndemână nu este
adăugat autor Jerry Coffin, sursa
@ kuroineko: Am editat răspunsul pentru a explica raționamentul.
adăugat autor Jerry Coffin, sursa
@Hossein: Din câte știu, singurele funcții din C ++ care iau șiruri de format în format printf sunt familia printf pe care C ++ "a mostenit-o" din C.
adăugat autor Jerry Coffin, sursa
@Programmer: vedeți stackoverflow.com/questions/12044357/… . Rezumat: de cele mai multe ori cout este mai lent, pentru că ați folosit std :: endl în cazul în care nu ar trebui.
adăugat autor Jerry Coffin, sursa
@ TheDarkIn1978: Probabil ați uitat să #include . VC ++ are unele ciudățenii în anteturile care vă permit să definiți un șir, dar nu îl trimiteți la cout , fără a include antetul .
adăugat autor Jerry Coffin, sursa
OK, îmi pare rău pentru comentariul tăios. Totuși, printf este destul de util pentru depanare, iar fluxurile, deși mult mai puternice, au dezavantajul că codul nu oferă nicio idee despre ieșirea reală. Pentru formatul de ieșire, printf este încă o alternativă viabilă și este păcat că ambele sisteme nu pot coopera mai bine. Doar opinia mea, bineînțeles.
adăugat autor kuroi neko, sursa
Aroganță tipică C ++ expert. Dacă există printf, de ce să nu-l folosiți?
adăugat autor kuroi neko, sursa
Un dezavantaj major al cout-ului față de printf este faptul că cout nu oferă (și printf) formatul bazat pe un șir de format. O astfel de formatare este esențială pentru internaționalizarea afirmațiilor care imprimă o propoziție care implică mai mult de o valoare, deoarece ordinea și plasarea valorilor din propoziție ar putea să trebuiască să varieze în funcție de limbajul cititorului uman al propoziției tipărite. Internaționalizarea implică mai mult decât aceasta, dar cu printf și un catalog de mesaje obțineți ceva util mult mai repede decât cu cout.
adăugat autor Louis Strous, sursa

utilizați myString.c_str() dacă doriți să utilizați un șir c-like (const char *) cu printf

0
adăugat

Principalul motiv este probabil că un șir C ++ este un struct care include o valoare a lungimii curente, nu doar adresa unei secvențe de caractere terminată de un octet. Printf și rudele sale se așteaptă să găsească o astfel de secvență, nu un struct și, prin urmare, se confundă cu șiruri de caractere C ++.

Vorbind pentru mine, cred că printf are un loc care nu poate fi umplut cu ușurință de caracteristicile sintactice C ++, la fel cum structurile de tabele din html au un loc care nu poate fi umplute cu ușurință de div. După cum a scris Dykstra mai târziu despre gata, el nu intenționa să înceapă o religie și într-adevăr se abținea de a nu-l folosi ca o limbă pentru a compensa codul prost proiectat.

Ar fi frumos dacă proiectul GNU ar adăuga familia printf la extensiile lor g ++.

0
adăugat

Utilizați std :: printf și c_str () exemplu:

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

Printf este de fapt destul de bun pentru a utiliza în cazul în care dimensiunea contează. În cazul în care executați un program în care memoria este o problemă, atunci printf este de fapt o soluție foarte bună și sub rater. Cout în mod esențial schimbă biții pentru a face spațiu pentru șir, în timp ce printf ia doar un fel de parametri și imprimă-l pe ecran. Dacă ați compila un simplu program de salut lume, Printf ar fi capabil să-l compileze în mai puțin de 60 000 de biți, spre deosebire de cout, ar fi nevoie de peste 1 milion de biți pentru a fi compilați.

Pentru situația dvs., ID-ul sugerează utilizarea cout pur și simplu pentru că este mult mai convenabil de utilizat. Deși, aș susține că printf este ceva bun de știut.

0
adăugat

printf accepts a variable number of arguments. Those can only have Plain Old Data (POD) types. Code that passes anything other than POD to printf only compiles because the compiler assumes you got your format right. %s means that the respective argument is supposed to be a pointer to a char. In your case it is an std::string not const char*. printf does not know it because the argument type goes lost and is supposed to be restored from the format parameter. When turning that std::string argument into const char* the resulting pointer will point to some irrelevant region of memory instead of your desired C string. For that reason your code prints out gibberish.

În timp ce printf este un alegere excelentă pentru imprimarea textului formatat , (mai ales dacă intenționați să aveți un strat de umplutură), poate fi periculos dacă nu ați activat avertismentele compilatorului. Activați întotdeauna avertismente , pentru că atunci greșelile de acest fel sunt ușor de evitat. Nu există niciun motiv pentru a utiliza mecanismul std :: cout în cazul în care familia printf poate face aceeași sarcină într-un mod mult mai rapid și mai frumos. Asigurați-vă că ați activat toate avertismentele ( -Wall -Wextra ) și veți fi buni. În cazul în care utilizați propria implementare personalizată printf , ar trebui să o declarați cu mecanismul __ attribute care permite compilatorului să verifice șirul de formate față de parametrii furnizați .

0
adăugat