C ++ printf с std :: string?

Мое понимание состоит в том, что string является членом пространства имен std , поэтому почему происходит следующее?

 #include  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 не является безопасным по типу, поскольку он использует переменные аргументы в смысле C 1 . printf не имеет опции для std::string , только для строки стиля C. Использование чего-то еще вместо того, что он ожидает, определенно не даст вам желаемых результатов. На самом деле это неопределенное поведение, поэтому может произойти что угодно.

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

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

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

 #include  #include  #include  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 , потому что сама printf просто не будет выполнять задание правильно.

  4. Хотя они довольно компактны, строки формата printf могут быть довольно нечитаемыми. Даже среди программистов C, которые используют 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 на самом деле является очень хорошим и под редким решением. Cout существенно сдвигает бит, чтобы освободить место для строки, в то время как printf просто принимает какие-то параметры и выводит их на экран. Если вы собираетесь составить простую программу приветствия, printf сможет скомпилировать ее менее чем за 60 000 бит, а не cout, для ее компиляции потребуется более 1 миллиона бит.

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

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

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

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

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

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

  • Строка C ++ для enums
  • Форматировать строки в методе Console.WriteLine
  • Как разбить одну строку на несколько строк, разделенных хотя бы одним пространством в оболочке bash?
  • Закрытие сканера вызывает java.util.NoSuchElementException
  • Android: что-то лучше, чем андроид: ellipsize = "end", чтобы добавить "..." в укороченные длинные строки?
  • Сортировка одной строки в Java
  • Как работает строковая подстрока в Swift
  • В C # следует ли использовать string.Empty или String.Empty или "" для инициализации строки?
  • Что такое возврат каретки, перевод строки и фид?
  • Проверьте, соответствует ли строка регулярному выражению в сценарии Bash
  • Конкатенация строк: оператор concat () vs "+"
  • Давайте будем гением компьютера.