带有std :: string的C++ printf?

The*_*978 144 c++ string printf namespaces std

我的理解是它stringstd命名空间的成员,为什么会出现以下情况呢?

#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;
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

每次程序运行时,都会myString打印一个看似随机的3个字符的字符串,例如上面的输出.

chr*_*ris 218

它正在编译,因为printf它不是类型安全的,因为它在C意义1中使用变量参数.printf没有选项std::string,只有C风格的字符串.用其他东西代替它所期望的东西肯定不会给你想要的结果.它实际上是未定义的行为,因此任何事情都可能发生.

解决这个问题的最简单方法是,因为你正在使用C++,所以正常打印它std::cout,因为std::string通过运算符重载支持:

std::cout << "Follow this command: " << myString;
Run Code Online (Sandbox Code Playgroud)

如果由于某种原因,您需要提取C样式字符串,则可以使用c_str()方法std::string获取以const char *null结尾的方法.使用你的例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

如果你想要一个类似printf但是类型安全的函数,请查看可变参数模板(C++ 11,从MSVC12开始支持所有主要编译器).你可以在这里找到一个例子.我知道在标准库中没有像这样的实现,但可能在Boost中,特别是boost::format.


[1]:这意味着您可以传递任意数量的参数,但函数依赖于您告诉它这些参数的数量和类型.在这种情况下printf,这意味着具有编码类型信息的字符串,如%d含义int.如果你撒谎的类型或数字,该功能没有标准的知道方式,虽然有些编译器能够检查并在你撒谎时发出警告.

  • 没有提到使用`cout`作为字符串? (2认同)

Jer*_*fin 40

请不要使用 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;
    
    Run Code Online (Sandbox Code Playgroud)

    无名语言环境("")根据用户的配置选择语言环境.因此,在我的机器上(配置为美国英语)打印出来123,456.78.对于那些为(例如)德国配置计算机的人来说,它会打印出类似的东西123.456,78.对于那些为印度配置它的人来说,它会打印出来1,23,456.78(当然还有很多其他的).随着printf我得到完全一个结果:123456.78.这是一致的,但对所有人来说都是错误的.基本上解决它的唯一方法是单独进行格式化,然后将结果作为字符串传递给printf,因为printf它本身根本无法正常工作.

  4. 虽然它们非常紧凑,但printf格式字符串可能非常难以理解.即使在printf几乎每天都在使用的C程序员中,我猜想至少有99%的人需要仔细研究以确定其中#%#x含义,以及它与其中#%#f含义有何不同(是的,它们意味着完全不同的东西) ).

  • @Jerry:只想指出使用printf比处理大数据时使用cout要快得多.因此,请不要说它没用:D (26认同)
  • 典型的C++专家傲慢.如果printf存在,为什么不使用它? (25认同)
  • @ TheDarkIn1978:你可能忘了`#include <string>`.VC++在其标题中有一些奇怪之处,它可以让你定义一个字符串,但不会将它发送到`cout`,而不包括`<string>`标题. (11认同)
  • @Programmer:请参阅http://stackoverflow.com/questions/12044357/printf-more-than-5-times-faster-than-stdcout.总结:大多数时候`cout`比较慢,这是因为你已经使用了`std :: endl`而你不应该这样做. (6认同)
  • 好的,对不起那个活泼的评论感到抱歉.尽管如此,printf对于调试来说非常方便,但是流程虽然功能更强大,却有一个缺点,即代码不会对实际输出有所了解.对于格式化输出,printf仍然是一个可行的替代方案,这是两个系统无法更好地合作的耻辱.当然只是我的意见. (6认同)
  • printf 是线程安全的(cout 不是),并且许多格式化使用 printf 比使用流(cout、stringstream 等)执行得更好。 (4认同)
  • @JerryCoffin 有人不厌其烦地写这个:https://msdn.microsoft.com/en-us/magazine/dn913181.aspx 显然它有它的粉丝:-) (3认同)
  • 当有一天您必须将大型软件的所有输出翻译为 i18n 时,您会真的非常讨厌那些教您使用 cout 的人。cout 是愚蠢的,不是无状态的(如果你使用一些修改 cout 状态的库,你最终会得到这样的代码“cout &lt;&lt; 64”输出“40”。像这样的代码`cout &lt;&lt;“The”&lt;&lt; color &lt;&lt; "house"` 是不可翻译的等等。 printf 没有任何这些设计缺陷。 (3认同)
  • @Hossein:据我所知,C++ 中唯一采用 printf 样式格式字符串的函数是 C++ 从 C “继承”的 `printf` 系列。 (2认同)
  • 与printf相比,cout的一个主要缺点是cout没有(和printf一样)提供基于格式字符串的格式.这种格式化对于打印涉及多个值的句子的语句的国际化是必不可少的,因为句子中的值的顺序和位置可能需要根据打印句子的人类读者的语言而变化.国际化涉及的不仅仅是这个,但是使用printf和消息目录,你得到的东西比cout快得多. (2认同)
  • @kuroineko 我 100% 同意你的笔记 _Typical..._ 在我看来,`cout` 是 C++ 提供的最糟糕的 _feature_ 。 (2认同)
  • 不,在启用所有警告开关的情况下使用“gcc”时。没有什么比这更安全、更方便了:100% 类型安全,检查参数计数。使用“cout”进行格式化是一件可怕的事情 - 特别是对于长日志文件条目。当 C++ _gurus_ 提出 `cout &lt;&lt; "Hallo"` 或一个数字时,我总是笑。但是用 30 个参数来做[正如我所说的日志]... (2认同)

Ale*_*ato 27

如果你想要一个与printf一起使用的类c字符串(const char*),请使用myString.c_str()


Ade*_*adi 6

使用std :: printf和c_str()示例:

std::printf("Follow this command: %s", myString.c_str());
Run Code Online (Sandbox Code Playgroud)