Насколько я понимаю, string
это член std
пространства имен, так почему же происходит следующее?
#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;
}
Каждый раз, когда программа запускается, myString
печатает, казалось бы, случайную строку из 3 символов, например, в выводе выше.
Ответы:
Он компилируется, потому что
printf
не является типобезопасным, поскольку использует переменные аргументы в смысле Си 1 .printf
не имеет опцииstd::string
, только строка в стиле C. Использование чего-то другого вместо того, что он ожидает, определенно не даст вам желаемых результатов. Это на самом деле неопределенное поведение, поэтому может случиться что угодно.Самый простой способ исправить это, так как вы используете C ++, это печатать его как обычно
std::cout
, посколькуstd::string
поддерживает это с помощью перегрузки операторов:Если по какой-то причине вам нужно извлечь строку в стиле C, вы можете использовать
c_str()
метод,std::string
чтобы получитьconst char *
нулевое окончание. Используя ваш пример:Если вам нужна функция, которая похожа
printf
, но безопасна по типу, изучите шаблоны с переменным числом аргументов (C ++ 11, поддерживается во всех основных компиляторах начиная с MSVC12). Вы можете найти пример одного здесь . Я ничего не знаю о такой реализации в стандартной библиотеке, но может быть в Boost, в частностиboost::format
.[1]: Это означает, что вы можете передать любое количество аргументов, но функция полагается на вас, чтобы сообщить количество и типы этих аргументов. В случае
printf
, это означает строку с закодированной информацией типа, такой как%d
значениеint
. Если вы лжете о типе или числе, функция не имеет стандартного способа узнать, хотя некоторые компиляторы имеют возможность проверять и выдавать предупреждения, когда вы лжете.источник
Пожалуйста, не используйте
printf("%s", your_string.c_str());
Используйте
cout << your_string;
вместо этого. Коротко, просто и небезопасно. На самом деле, когда вы пишете на C ++, вы, как правило, хотитеprintf
полностью избежать - это остаток от C, который редко нужен или полезен в C ++.Что касается того, почему вы должны использовать
cout
вместоprintf
, причины многочисленны. Вот несколько самых очевидных примеров:printf
не является типобезопасным. Если передаваемый вами тип отличается от указанного в спецификаторе преобразования,printf
он попытается использовать все, что найдет в стеке, как если бы это был указанный тип, что приведет к неопределенному поведению. Некоторые компиляторы могут предупреждать об этом при некоторых обстоятельствах, но некоторые компиляторы не могут / не будут вообще, и ни один не может при любых обстоятельствах.printf
не расширяемый Вы можете передавать ему только примитивные типы. Набор спецификаторов преобразования, который он понимает, жестко запрограммирован в его реализации, и вы не сможете добавить больше / другие. Большинство хорошо написанных C ++ должны использовать эти типы прежде всего для реализации типов, ориентированных на решаемую проблему.Это делает приличное форматирование намного сложнее. В качестве очевидного примера, когда вы печатаете числа для чтения людьми, вы, как правило, хотите вставлять тысячи разделителей через каждые несколько цифр. Точное количество цифр и символов, используемых в качестве разделителей, варьируется, но
cout
оно также охватывается. Например:Безымянный языковой стандарт («») выбирает языковой стандарт на основе конфигурации пользователя. Поэтому на моей машине (настроенной для английского языка США) это распечатывается как
123,456.78
. Для кого-то, чей компьютер настроен для (скажем) Германии, это распечатало бы что-то вроде123.456,78
. Для кого-то с настроенным для Индии, он будет распечатан как1,23,456.78
(и, конечно, есть много других). Сprintf
I получить ровно один результат:123456.78
. Это последовательно, но это всегда неправильно для всех и везде. По сути, единственный способ обойти это состоит в том, чтобы выполнить форматирование отдельно, а затем передать результат в виде строкиprintf
, так какprintf
сам по себе просто не будет выполнять работу правильно.printf
строки формата могут быть совершенно нечитаемыми. Даже среди программистов C , которые используютprintf
практически каждый день, я думаю , по крайней мере , 99% нужно будет искать вещи , чтобы быть уверенным , что#
в%#x
средства, и как это отличается от того , что#
в%#f
средства (и да, они имеют в виду совершенно разные вещи ).источник
#include <string>
. В заголовках VC ++ есть некоторые странности, которые позволяют вам определять строку, но не отправлять ееcout
, не включая<string>
заголовок.cout
происходит медленнее, потому что вы использовалиstd::endl
там, где не должны.используйте,
myString.c_str()
если вы хотите, чтобы c-like string (const char*
) использовалась с printfСпасибо
источник
Используйте пример std :: printf и c_str ():
источник
Основная причина, вероятно, заключается в том, что строка C ++ - это структура, которая включает значение текущей длины, а не только адрес последовательности символов, оканчивающейся на 0 байт. Printf и его родственники ожидают найти такую последовательность, а не структуру, и поэтому запутываются в строках C ++.
Говоря сам за себя, я считаю, что printf имеет место, которое не может быть легко заполнено синтаксическими функциями C ++, так же как структуры таблиц в html имеют место, которое не может быть легко заполнено элементами div. Как Дикстра писал позже о гото, он не собирался создавать религию и на самом деле только спорил против использования ее в качестве клэджа, чтобы восполнить плохо спроектированный код.
Было бы неплохо, если бы проект GNU добавил семейство printf к своим расширениям g ++.
источник
Printf на самом деле довольно хорошо использовать, если размер имеет значение. Это означает, что если вы запускаете программу, в которой проблема с памятью, то printf на самом деле является очень хорошим и менее подходящим решением. Cout существенно сдвигает биты, чтобы освободить место для строки, в то время как printf просто принимает какие-то параметры и выводит их на экран. Если вы скомпилируете простую программу hello world, printf сможет скомпилировать ее менее чем в 60 000 бит, в отличие от cout, для компиляции потребуется более 1 миллиона бит.
Для вашей ситуации id предлагает использовать cout просто потому, что его гораздо удобнее использовать. Хотя я бы сказал, что printf - это то, что нужно знать.
источник
printf
принимает переменное количество аргументов. У них могут быть только типы простых старых данных (POD). Код, который передает что-либо кроме PODprintf
только для компиляции, потому что компилятор предполагает, что вы правильно выбрали формат.%s
означает, что соответствующий аргумент должен быть указателем наchar
. В вашем случае этоstd::string
не такconst char*
.printf
не знает этого, потому что тип аргумента теряется и должен быть восстановлен из параметра формата. Превращая этотstd::string
аргумент вconst char*
результирующий указатель будет указывать на некоторую нерелевантную область памяти вместо желаемой строки C. По этой причине ваш код печатает бред.Хотя
printf
это отличный выбор для распечатки отформатированного текста (особенно , если вы собираетесь иметь отступы), это может быть опасно , если вы не включили предупреждения компилятора. Всегда включайте предупреждения, потому что таких ошибок легко избежать. Нет смысла использовать неуклюжийstd::cout
механизм, еслиprintf
семья может выполнять ту же задачу намного быстрее и красивее. Просто убедитесь, что вы включили все предупреждения (-Wall -Wextra
), и все будет хорошо. В случае, если вы используете свою собственную пользовательскуюprintf
реализацию, вы должны объявить ее с помощью__attribute__
механизма, который позволяет компилятору сверять строку формата с предоставленными параметрами .источник