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)

вероятно, состоит в том, что строка C ++ - это структура, которая включает значение текущей длины, а не только адрес последовательности символов, оканчивающейся на 0 байт. Printf и его родственники ожидают найти такую последовательность, а не структуру, и поэтому запутываются в строках C ++.

Говоря сам за себя, я считаю, что printf имеет место, которое не может быть легко заполнено синтаксическими функциями C ++, так же как структуры таблиц в html имеют место, которое не может быть легко заполнено элементами div. Как Дикстра писал позже о гото, он не собирался создавать религию и на самом деле только спорил против использования ее в качестве клэджа, чтобы восполнить плохо разработанный код.

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

Используйтеstd :: Printfример @ и c_str ():

std::printf("Follow this command: %s", myString.c_str());

если вы хотите использовать c-подобную строку (const char *) для printf

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

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

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

Если по какой-то причине вам нужно извлечь строку в стиле C, вы можете использоватьc_str() методstd::string чтобы получитьconst char * с нулевым символом в конце. Используя ваш пример:

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

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

если размер имеет значение. Это означает, что если вы запускаете программу, в которой проблема с памятью, то printf на самом деле является очень хорошим и менее подходящим решением. Cout существенно сдвигает биты, чтобы освободить место для строки, в то время как printf просто принимает какие-то параметры и выводит их на экран. Если вы скомпилируете простую программу hello world, printf сможет скомпилировать ее менее чем в 60 000 бит, в отличие от cout, для компиляции потребуется более 1 миллиона бит.

Для вашей ситуации id предлагает использовать cout просто потому, что его гораздо удобнее использовать. Хотя я бы сказал, что printf - это то, что нужно знать.

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

В то время какprintf является отличный выбор для печати форматированного текста, (особенно если вы собираетесь использовать отступы), это может быть опасно, если вы не включили предупреждения компилятора. Всегда включайте предупреждения потому что таких ошибок легко избежать. Нет причин использовать неуклюжийstd::cout механизм, еслиprintf family может выполнить ту же задачу намного быстрее и красивее. Просто убедитесь, что вы включили все предупреждения -Wall -Wextra) и тебе будет хорошо. Если вы используете свой собственный пользовательскийprintf реализации вы должны объявить это с__attribute__ механизм, который позволяет компилятору проверять строку формата по предоставленным параметрам.

ВАШ ОТВЕТ НА ВОПРОС