C ++的printf与STD ::string?

我的理解是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; } 

在这里输入图像描述

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

它是编译的,因为printf不是types安全的,因为它在C中使用可变参数1printf没有std::string选项,只有C风格的string。 用别的东西代替它期望的东西绝对不会给你想要的结果。 这实际上是不确定的行为,所以什么都可能发生。

最简单的方法来解决这个问题,因为你使用C ++,通常使用std::cout来打印它,因为std::string支持通过操作符重载:

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

如果出于某种原因需要提取C风格的string,则可以使用std::stringc_str()方法来获取一个以空const char * std::stringconst char * 。 用你的例子:

 #include <iostream> #include <string> 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这样的printf ,但是types安全,请查看可变参数模板(C ++ 11,所有主要编译器都支持MSVC12)。 你可以在这里find一个例子。 没有什么我知道在标准库中的实现,但可能在Boost中,特别是boost::format


[1]:这意味着你可以传递任意数量的参数,但函数依赖于你告诉它的数量和types的参数。 在printf的情况下,这意味着一个编码types信息的string,如%d表示int 。 如果你说谎的types或编号,这个函数没有标准的认识方式,虽然有些编译器有能力在你说谎时检查和发出警告。

请不要使用printf("%s", your_string.c_str());

使用cout << your_string; 代替。 简单,简单和types安全。 事实上,当你编写C ++时,你通常希望完全避免使用printf ,这是C ++中的一个剩余部分,在C ++中很less需要或者很有用。

至于你为什么要用cout而不是printf ,原因很多。 以下是一些最明显的例子:

  1. 正如问题所示, printf不是types安全的。 如果您传递的types与转换说明符中给出的types不同,那么printf会尝试使用它在堆栈上find的任何东西,就像它是指定的types一样,给出未定义的行为。 有些编译器在某些情况下可能会提出这样的警告,但是有些编译器根本不可能/根本不可能,在任何情况下都不能。
  2. printf是不可扩展的。 你只能传递原始types。 它理解的转换说明符的集合在其实现中是硬编码的,并且没有办法添加更多/其他的。 大多数写得很好的C ++应该主要使用这些types来实现面向解决问题的types。
  3. 这使体面的格式化变得更加困难。 举个明显的例子,当你打印数字供读者阅读时,你通常要插入数千个分隔符。 数字的确切位数和用作分隔符的字符各不相同,但是cout也包括cout 。 例如:

     std::locale loc(""); std::cout.imbue(loc); std::cout << 123456.78; 

    无名的语言环境(“”)根据用户的configurationselect一个语言环境。 因此,在我的机器上(configuration为美国英语),打印出123,456.78 。 对于有德国电脑configuration的人来说,它会打印出123.456,78这样的123.456,78 。 对于configuration了印度的人来说,它会打印出1,23,456.78 (当然还有很多其他的)。 用printf我得到了一个结果: 123456.78 。 这是一致的,但对任何地方的人来说都是一贯的错误 。 基本上,解决这个问题的唯一方法是分别进行格式化,然后将结果作为string传递给printf ,因为printf本身不会正确地完成这项工作。

  4. 虽然它们非常紧凑,但是printf格式的string可能是不可读的。 即使在每天使用printf C程序员中,我猜也至less有99%的人需要去查看,以确定%#x#是什么意思,以及与%#f#什么不同(是的,他们是完全不同的东西)。

如果你想要一个类似c的string(const char *)和printf一起使用,可以使用myString.c_str()

主要的原因可能是C ++string是一个包含当前长度值的结构,而不仅仅是由0字节终止的字符序列的地址。 Printf及其亲戚希望find这样一个序列,而不是一个结构体,因此被C ++string弄糊涂了。

说起我自己,我相信printf有一个地方不容易被C ++语法特征所填充,就像html中的表结构有一个不容易被div填充的地方一样。 正如Dykstra后来写到goto,他并不打算启动一个宗教,实际上只是在反对用它来弥补devise不佳的代码。

如果GNU项目将printf系列添加到他们的g ++扩展中,这将是相当不错的。

如果大小很重要的话,printf实际上是相当不错的。 这意味着如果你运行的是内存问题的程序,那么printf实际上是一个很好的解决scheme。 Cout本质上是为了为string腾出空间而创build空间,而printf只是带入某些参数并将其输出到屏幕上。 如果要编译一个简单的hello世界程序,printf将能够以小于60,000位的比特编译它,而不是cout,编译将需要超过100万个比特。

对于你的情况,编号build议使用cout只是因为它更方便使用。 虽然,我会争辩说printf是一件很好的事情。

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

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

printf接受可变数目的参数。 那些只能有简单的旧数据(POD)types。 只有POD才能传递给printf只能编译,因为编译器假定你的格式正确。 %s表示相应的参数应该是一个指向char的指针。 在你的情况下,它是一个std::string不是const char*printf不知道它是因为参数types丢失,应该从format参数中恢复。 当将std::string参数转换为const char* ,生成的指针将指向一些不相关的内存区域,而不是所需的Cstring。 出于这个原因,你的代码打印出乱码。

虽然printf是打印格式化文本的绝佳select (特别是如果打算填充),但如果未启用编译器警告,则可能会非常危险。 总是启用警告,因为这样的错误很容易避免。 如果printf系列可以以更快更漂亮的方式执行相同的任务,那么没有理由使用笨拙的std::cout机制。 只要确保你已经启用所有的警告( -Wall -Wextra ),你会很好。 如果您使用自己的自定义printf实现,则应该使用__attribute__机制声明它, 使编译器能够根据提供的参数检查格式string 。