printf with std :: string?

时间:2012-06-02 21:07:06

标签: c++ string namespaces printf 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;
}

enter image description here

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

7 个答案:

答案 0 :(得分:208)

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

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

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

如果由于某种原因,您需要提取C样式字符串,则可以使用c_str()的{​​{1}}方法获取以null结尾的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; } 的函数,但输入类型为safe,请查看可变参数模板(C ++ 11,从MSVC12开始支持所有主要编译器)。您可以找到一个here的示例。我不知道在标准库中实现了什么,但可能在Boost中,特别是boost::format


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

答案 1 :(得分: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;
    

    无名区域设置(“”)根据用户的配置选择区域设置。因此,在我的机器上(配置为美国英语),打印出123,456.78。对于那些为(例如)德国配置计算机的人来说,它会打印出像123.456,78这样的东西。对于为印度配置它的人来说,它将打印为1,23,456.78(当然还有许多其他人)。使用printf我只得到一个结果:123456.78。这是一致的,但对于每个人来说,它始终是错误的。基本上解决它的唯一方法是单独进行格式化,然后将结果作为字符串传递给printf,因为printf本身只是正确地完成工作

  4. 虽然它们非常紧凑,但printf格式字符串可能非常难以理解。即使在几乎每天都使用printf的C程序员中,我猜至少有99%的人需要查看以确定#%#x的含义,以及不同于#%#f的含义(是的,它们的意思完全不同)。

答案 2 :(得分:24)

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

答案 3 :(得分:6)

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

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

答案 4 :(得分:2)

如果尺寸很重要,Printf实际上非常好用。这意味着如果你正在运行一个存在内存问题的程序,那么printf实际上是一个非常好的并且在评估者之下。 Cout本质上将位移位以为字符串腾出空间,而printf只接受某种参数并将其打印到屏幕上。如果你要编译一个简单的hello world程序,printf将能够以小于60,000位编译它而不是cout,它将需要超过100万位才能编译。

根据您的情况,id建议使用cout只是因为它使用起来更方便。虽然,我认为printf是一件好事。

答案 5 :(得分:1)

主要原因可能是C ++字符串是包含当前长度值的结构,而不仅仅是由0字节终止的字符序列的地址。 Printf及其亲属期望找到这样的序列,而不是结构,因此被C ++字符串搞糊涂。

对我自己说,我相信printf有一个不容易被C ++语法特征填充的地方,就像html中的表格结构有一个不容易被div填充的地方一样。正如Dykstra后来写到关于goto的内容,他并不打算开始一个宗教,并且实际上只是反对将它用作弥补设计不佳的代码的kludge。

如果GNU项目将printf系列添加到他们的g ++扩展中,那将是非常好的。

答案 6 :(得分:1)

printf接受可变数量的参数。那些只能有普通旧数据(POD)类型。将POD以外的任何内容传递给printf的代码仅编译,因为编译器假定您的格式正确。 %s表示相应的参数应该是指向char的指针。在您的情况下,它是std::string而不是const char*printf不知道它,因为参数类型丢失并且应该从format参数恢复。将std::string参数转换为const char*时,生成的指针将指向一些不相关的内存区域而不是您想要的C字符串。因此,您的代码会打印出乱码。

虽然printfan excellent choice for printing out formatted text,(特别是如果您打算使用填充),但如果您没有启用编译器警告,则会很危险。 始终启用警告,因为这样的错误很容易避免。如果std::cout系列能够以更快,更漂亮的方式完成相同的任务,则没有理由使用笨拙的printf机制。只需确保您已启用所有警告(-Wall -Wextra),您就会很好。如果您使用自己的自定义printf实施,则应使用enables the compiler to check the format string against the parameters provided__attribute__机制声明它。