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 117

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

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

  4. хотя они довольно компактны, printf формат строки могут быть совершенно нечитабельными. Даже среди программистов, которые используют printf практически каждый день, я бы предположил, что по крайней мере 99% нужно будет искать вещи чтобы быть уверенным, что # in %#x значит, и чем это отличается от того, что # in %#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. Как позже писал Дикстра о goto, он не собирался начинать религию и на самом деле только выступал против использования ее в качестве kludge, чтобы компенсировать плохо разработанный код.

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

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