Rozumiem, że string
jest to element członkowski std
przestrzeni nazw, więc dlaczego ma miejsce następujący przebieg?
#include <iostream>
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;
}
Za każdym razem, gdy program jest uruchamiany, myString
wyświetla pozornie losowy ciąg 3 znaków, tak jak na powyższym wyjściu.
Odpowiedzi:
Kompiluje się, ponieważ
printf
nie jest bezpieczny dla typów, ponieważ używa argumentów zmiennych w sensie C 1 .printf
nie ma opcji dlastd::string
, tylko łańcuch w stylu C. Używanie czegoś innego zamiast tego, czego się oczekuje, z pewnością nie przyniesie oczekiwanych rezultatów. To właściwie nieokreślone zachowanie, więc wszystko może się zdarzyć.Najłatwiejszym sposobem rozwiązania tego problemu, ponieważ używasz C ++, jest wydrukowanie go normalnie
std::cout
, ponieważstd::string
obsługuje to poprzez przeciążenie operatorów:Jeśli z jakiegoś powodu musisz wyodrębnić napis w stylu C, możesz użyć
c_str()
metody of,std::string
aby uzyskać ciągconst char *
zakończony znakiem null. Na podstawie twojego przykładu:Jeśli potrzebujesz funkcji, która jest podobna
printf
, ale bezpieczna, spójrz na różne szablony (C ++ 11, obsługiwane we wszystkich głównych kompilatorach od MSVC12). Możesz znaleźć przykład jednego tutaj . Nie ma nic, co wiem o takiej implementacji w standardowej bibliotece, ale konkretnie może istnieć w Boostboost::format
.[1]: Oznacza to, że możesz przekazać dowolną liczbę argumentów, ale funkcja polega na tym, że podasz jej liczbę i typy tych argumentów. W przypadku
printf
, oznacza to ciąg z zakodowanymi informacjami o typie, takimi jak%d
znaczenieint
. Jeśli kłamiesz na temat typu lub liczby, funkcja nie ma standardowego sposobu poznania, chociaż niektóre kompilatory mają możliwość sprawdzania i ostrzegania, gdy kłamiesz.źródło
Proszę nie używać
printf("%s", your_string.c_str());
Użyj
cout << your_string;
zamiast tego. Krótkie, proste i bezpieczne. W rzeczywistości, pisząc w C ++, generalnie chceszprintf
całkowicie tego uniknąć - jest to pozostałość po C, która jest rzadko potrzebna lub przydatna w C ++.Jeśli chodzi o to, dlaczego powinieneś używać
cout
zamiastprintf
, powodów jest wiele. Oto kilka z najbardziej oczywistych:printf
nie jest bezpieczny. Jeśli typ, który przekazujesz różni się od tego podanego w specyfikatorze konwersji,printf
spróbuje użyć wszystkiego, co znajdzie na stosie, tak jakby był to określony typ, dając niezdefiniowane zachowanie. Niektóre kompilatory mogą ostrzec o tym w pewnych okolicznościach, ale niektóre kompilatory nie mogą / nie chcą w ogóle, a żaden nie może w każdych okolicznościach.printf
nie jest rozszerzalny. Możesz przekazać do niego tylko typy pierwotne. Zbiór specyfikatorów konwersji, który rozumie, jest zakodowany na stałe w swojej implementacji i nie ma możliwości dodania więcej / innych. Większość dobrze napisanych C ++ powinno używać tych typów głównie do implementacji typów zorientowanych na rozwiązywany problem.To znacznie utrudnia porządne formatowanie. Na przykład, gdy drukujesz liczby do przeczytania, zazwyczaj chcesz wstawiać separatory tysięcy co kilka cyfr. Dokładna liczba cyfr i znaków używanych jako separatory jest różna, ale
cout
obejmuje to również. Na przykład:Bezimienne ustawienia regionalne („”) wybierają ustawienia narodowe na podstawie konfiguracji użytkownika. Dlatego na moim komputerze (skonfigurowanym dla języka angielskiego w USA) jest to drukowane jako
123,456.78
. Dla kogoś, kto ma swój komputer skonfigurowany dla (powiedzmy) Niemiec, wydrukowałby coś takiego123.456,78
. Dla kogoś, kto skonfigurowałby go dla Indii, wydrukowałby go jako1,23,456.78
(i oczywiście jest wiele innych). Zprintf
otrzymuję dokładnie jeden wynik:123456.78
. Jest spójny, ale zawsze jest zły dla wszystkich i wszędzie. Zasadniczo jedynym sposobem obejścia tego jest oddzielne formatowanie, a następnie przekazanie wyniku w postaci ciągu znakówprintf
, ponieważprintf
samo po prostu nie wykona zadania poprawnie.printf
ciągi formatujące mogą być dość nieczytelne. Nawet wśród programistów C, którzy używająprintf
praktycznie każdego dnia, przypuszczam, że co najmniej 99% musiałoby sprawdzić, co oznacza#
in%#x
i czym różni się od tego, co oznacza#
in%#f
(i tak, mają na myśli zupełnie inne rzeczy ).źródło
#include <string>
. VC ++ ma pewne dziwactwa w swoich nagłówkach, które pozwolą ci zdefiniować ciąg, ale nie wysyłaj go do niegocout
, bez dołączania<string>
nagłówka.cout
jest to wolniejsze, ponieważ używałeśstd::endl
tam, gdzie nie powinieneś.użyj,
myString.c_str()
jeśli chcesz, aby string (const char*
) podobny do c był używany z printfdzięki
źródło
Użyj przykładu std :: printf i c_str ():
źródło
Głównym powodem jest prawdopodobnie to, że łańcuch w C ++ jest strukturą zawierającą wartość bieżącej długości, a nie tylko adres sekwencji znaków zakończonych bajtem 0. Printf i jego krewni spodziewają się znaleźć taką sekwencję, a nie strukturę, i dlatego są zdezorientowani przez łańcuchy C ++.
Mówiąc za siebie, uważam, że printf ma miejsce, którego nie da się łatwo wypełnić funkcjami składniowymi C ++, tak jak struktury tabel w html mają miejsce, które nie może być łatwo wypełnione przez elementy div. Jak pisał później Dykstra o goto, nie miał zamiaru zakładać religii i tak naprawdę argumentował tylko przeciwko używaniu jej jako głupoty do zrekompensowania źle zaprojektowanego kodu.
Byłoby całkiem fajnie, gdyby projekt GNU dodał rodzinę printf do ich rozszerzeń g ++.
źródło
Printf jest całkiem niezły w użyciu, jeśli rozmiar ma znaczenie. Oznacza to, że jeśli uruchamiasz program, w którym problem dotyczy pamięci, printf jest w rzeczywistości bardzo dobrym i słabo ocenianym rozwiązaniem. Cout zasadniczo przesuwa bity, aby zrobić miejsce na łańcuch, podczas gdy printf po prostu pobiera jakieś parametry i wyświetla je na ekranie. Gdybyś miał skompilować prosty program hello world, printf byłby w stanie skompilować go w mniej niż 60 000 bitów, w przeciwieństwie do cout, kompilacja zajęłaby ponad 1 milion bitów.
W twojej sytuacji id sugeruje użycie cout tylko dlatego, że jest znacznie wygodniejszy w użyciu. Chociaż uważałbym, że printf to coś, co warto wiedzieć.
źródło
printf
akceptuje zmienną liczbę argumentów. Mogą mieć tylko typy zwykłych starych danych (POD). Kod, który przekazuje coś innego niż POD,printf
tylko kompiluje się, ponieważ kompilator zakłada, że masz właściwy format.%s
oznacza, że odpowiedni argument ma być wskaźnikiem do achar
. W twoim przypadku takstd::string
nie jestconst char*
.printf
nie wie o tym, ponieważ typ argumentu ginie i powinien zostać przywrócony z parametru format. Podczas obracania tegostd::string
argument wconst char*
wynikowy wskaźnik wskaże na jakiś nieistotny region pamięci zamiast żądanego ciągu C. Z tego powodu Twój kod wyświetla bełkot.Chociaż
printf
jest to doskonały wybór do drukowania sformatowanego tekstu (szczególnie jeśli zamierzasz mieć dopełnienie), może być niebezpieczny, jeśli nie włączyłeś ostrzeżeń kompilatora. Zawsze włączaj ostrzeżenia, ponieważ takie błędy można łatwo uniknąć. Nie ma powodu, aby stosować niezdarnystd::cout
mechanizm, jeśliprintf
rodzina może wykonać to samo zadanie w znacznie szybszy i ładniejszy sposób. Po prostu upewnij się, że włączyłeś wszystkie ostrzeżenia (-Wall -Wextra
) i wszystko będzie dobrze. W przypadku korzystania z własnej niestandardowejprintf
implementacji należy zadeklarować ją za pomocą__attribute__
mechanizmu, który umożliwia kompilatorowi sprawdzenie ciągu formatu z podanymi parametrami .źródło