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

enter image description here

каждый раз, когда программа работает, myString печатает, казалось бы, случайную строку из 3 символов, например, в выводе выше.

7 ответов


это компиляция, потому что printf не является безопасным типом, так как он использует переменные аргументы в смысле c1. printf нет вариант std::string, только строка в стиле C. Использование чего-то другого вместо того, что он ожидает, определенно не даст вам желаемых результатов. На самом деле это неопределенное поведение, поэтому может произойти что угодно.

самый простой способ исправить это, так как вы используете C++, печатает его обычно с std::cout С std::string поддерживает, что через перегрузку оператора:

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

если по какой-то причине вам нужно извлечь строку C-style, вы можете использовать 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, но тип safe, посмотрите на вариадические шаблоны (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, причин множество. Вот выборка из нескольких наиболее очевидных:

  1. как показывает вопрос,printf не является типобезопасным. Если тип, который вы передаете, отличается от указанного в описателе преобразования, printf попытается использовать все, что он найдет в стеке, как если бы это был указанный тип, давая неопределенное поведение. Некоторые компиляторы могут предупредить об этом при определенных обстоятельствах, но некоторые компиляторы не могут/не будут вообще, и никто не может при всех обстоятельствах.
  2. printf не расширяемый. Вы можете передать ему только примитивные типы. Набор спецификаторов преобразования, который он понимает, жестко закодирован в его реализации, и нет никакого способа для вас, чтобы добавить дополнительные/другие. Большинство хорошо написанных C++ должны использовать эти типы в первую очередь для реализации типов, ориентированных на решаемую проблему.
  3. это делает достойное форматирование намного сложнее. Например, при печати чисел для чтения обычно требуется вставить тысячи разделителей через каждые несколько цифр. Точное количество цифр и символов, используемых в качестве разделителей, варьируется, но cout это тоже покрыто. Для пример:

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

    безымянная локаль ( "" ) выбирает локаль на основе конфигурации пользователя. Поэтому на моей машине (настроенной для US English) это печатается как 123,456.78. Для кого-то, у кого есть компьютер, настроенный для (скажем) Германии, он распечатает что-то вроде 123.456,78. Для кого-то с ним, настроенным для Индии, он будет распечатан как 1,23,456.78 (и, конечно, есть много других). С printf Я получаю ровно один результат: 123456.78. Она последовательна, но это последовательно неправильно для всех, везде. По сути, единственный способ обойти это-сделать форматирование отдельно, потом передать результат в виде строки printf, потому что просто не сделать работу правильно.

  4. хотя они довольно компактны, printf строки формата могут быть совершенно нечитаемыми. Даже среди программистов, которые используют printf практически каждый день, я бы предположил, что по крайней мере 99% нужно будет искать вещи чтобы быть уверенным, что # на %#x означает, и как это отличается от того, что # на %#f означает (и да, они означают совершенно разные вещи).

используйте myString.c_str () если вы хотите использовать C-подобную строку (const char*) с printf


использовать 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. Как позже писал Дикстра о Гото, он не собирался основывать религию и на самом деле только возражал против использования ее в качестве клуджа, чтобы компенсировать плохо разработанный код.

было бы неплохо, если бы проект GNU добавил семейство printf в свои расширения g++.


printf принимает переменное количество аргументов. Они могут иметь только простые старые типы данных (POD). Код, который передает все, кроме POD в printf компилируется только потому, что компилятор предполагает, что вы получили правильный формат. %s означает, что соответствующий аргумент должен быть указателем на char. В вашем случае это std::string не const char*. printf не знает этого, потому что тип аргумента теряется и должен быть восстановлен из параметра format. При повороте это