C++ printf с std:: string?
мое понимание заключается в том, что 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 символов, например, в выводе выше.
7 ответов:
это компиляция, потому что
printf
не является типобезопасным, так как он использует переменные аргументы в смысле C1.printf
не имеет возможности дляstd::string
, только строка C-стиля. Использование чего-то другого вместо того, что он ожидает, определенно не даст вам желаемых результатов. Это на самом деле неопределенное поведение, так что все может случиться.самый простой способ исправить это, так как вы используете C++, печатает его обычно с
std::cout
Сstd::string
поддерживает, что через перегрузку оператора:std::cout << "Follow this command: " << myString;
если по какой-то причине вам нужно извлечь строку C-стиля, вы можете использовать
c_str()
методstd::string
иconst char *
это null-terminated. Используя Ваш пример:#include <iostream> #include <string> #include <stdio.h> 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; }
если вы хотите функцию, которая похожа на
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
это тоже покрыто. Для пример:std::locale loc(""); std::cout.imbue(loc); std::cout << 123456.78;
безымянная локаль ( "" ) выбирает локаль на основе конфигурации пользователя. Поэтому на моей машине (настроенной для нас на английском языке) это печатается как
123,456.78
. Для кого-то, у кого есть свой компьютер, настроенный для (скажем) Германии, он будет печатать что-то вроде123.456,78
. Для кого-то с ним настроен для Индии, он будет печатать как1,23,456.78
(и, конечно, есть много других). Сprintf
Я получаю ровно один результат:123456.78
. Она последовательна, но это последовательно неправильно для всех и везде. По сути, единственный способ обойти это-сделать форматирование отдельно, потом передать результат в виде строкиprintf
, потому что просто не сделать работу правильно.- хотя они довольно компактны,
printf
формат строки могут быть совершенно нечитабельными. Даже среди программистов, которые используютprintf
практически каждый день, я бы предположил, что по крайней мере 99% нужно будет искать вещи чтобы быть уверенным, что#
in%#x
значит, и чем это отличается от того, что#
in%#f
означает (и да, они означают совершенно разные вещи).
использовать std:: printf и c_str() пример:
std::printf("Follow this command: %s", myString.c_str());
Printf на самом деле довольно хорошо использовать, если размер имеет значение. Это означает, что если вы запускаете программу, где память является проблемой, то printf на самом деле очень хорошее и под rater решение. Cout существенно сдвигает биты, чтобы освободить место для строки, в то время как printf просто принимает какие-то параметры и печатает их на экране. Если бы вы скомпилировали простую программу hello world, printf смог бы скомпилировать ее менее чем за 60 000 бит, в отличие от cout, это заняло бы более 1 миллиона биты для компиляции.
для вашей ситуации id предлагает использовать cout просто потому, что это гораздо удобнее в использовании. Хотя, я бы сказал, что printf-это что-то хорошее, чтобы знать.
основная причина, вероятно, заключается в том, что строка C++ является структурой, которая включает в себя значение текущей длины, а не только адрес последовательности символов, заканчивающихся 0 байтом. Printf и его родственники ожидают найти такую последовательность, а не структуру, и поэтому путаются в строках C++.
говоря за себя, я считаю, что printf имеет место, которое не может быть легко заполнено синтаксическими функциями C++, так же как табличные структуры в html имеют место, которое не может быть легко заполнено divs. Как позже писал Дикстра о goto, он не собирался начинать религию и на самом деле только выступал против использования ее в качестве kludge, чтобы компенсировать плохо разработанный код.
было бы неплохо, если бы проект GNU добавил семейство printf в свои расширения g++.
printf
принимает переменное число аргументов. Они могут иметь только простые старые типы данных (POD). Код, который передает все, кроме POD вprintf
компилируется только потому, что компилятор предполагает, что вы получили правильный формат.%s
означает, что соответствующий аргумент должен быть указателем наchar
. В вашем случае этоstd::string
неconst char*
.printf
не знает этого, потому что тип аргумента теряется и должен быть восстановлен из параметра format. При повороте это