我的理解是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;
}
每次程序运行时,myString
都会打印一个看似随机的3个字符的字符串,例如在上面的输出中。
它正在编译,因为printf
不是类型安全的,因为它在C sense1中使用变量参数。 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
但类型安全的函数,请查看可变参数模板(C ++ 11,从MSVC12开始支持所有主要编译器)。你可以找到一个here的例子。我不知道在标准库中实现了什么,但可能在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
格式字符串可能非常难以理解。即使在几乎每天都使用printf
的C程序员中,我猜想至少有99%的人需要查看以确定#
中的%#x
意味着什么,以及它与#
中%#f
的含义有何不同(是的,他们的意思完全不同)。如果你想要一个类似c的字符串(myString.c_str()
)与printf一起使用,请使用const char*
谢谢
使用std::printf和c_str()示例:
std::printf("Follow this command: %s", myString.c_str());
如果尺寸很重要,Printf实际上非常好用。这意味着如果你正在运行一个存在内存问题的程序,那么printf实际上是一个非常好的并且在评估者之下。 Cout本质上将位移位以为字符串腾出空间,而printf只接受某种参数并将其打印到屏幕上。如果你要编译一个简单的hello world程序,printf将能够以小于60,000位编译它而不是cout,它将需要超过100万位才能编译。
对于你的情况,id建议使用cout只是因为它使用起来更方便。虽然,我认为printf是一件好事。
主要原因可能是C ++字符串是一个包含当前长度值的结构,而不仅仅是由0字节终止的字符序列的地址。 Printf及其亲属期望找到这样的序列,而不是结构,因此被C ++字符串搞糊涂了。
对我自己说,我相信printf有一个不容易被C ++语法特征填充的地方,就像html中的表结构有一个不容易被div填充的地方。正如戴克斯特拉后来写的关于goto,他并不打算开始一个宗教,并且真的只是反对用它作为弥补设计不佳的代码的kludge。
如果GNU项目将printf系列添加到他们的g ++扩展中,那将是相当不错的。
printf
接受可变数量的参数。那些只能有普通旧数据(POD)类型。将POD以外的任何内容传递给printf
的代码只能编译,因为编译器假定您的格式正确。 %s
意味着相应的参数应该是指向char
的指针。在你的情况下,这是一个std::string
而不是const char*
。 printf
不知道它,因为参数类型丢失并且应该从format参数恢复。当将std::string
参数转换为const char*
时,结果指针将指向一些不相关的内存区域而不是您想要的C字符串。出于这个原因,你的代码打印出乱码。
虽然printf
是an excellent choice for printing out formatted text,(特别是如果你打算填充),如果你没有启用编译器警告可能会很危险。始终启用警告,因为这样的错误很容易避免。如果std::cout
家族能够以更快,更漂亮的方式完成同样的任务,没有理由使用笨拙的printf
机制。只要确保你已经启用了所有警告(-Wall -Wextra
),你就会很好。如果您使用自己的自定义printf
实现,您应该使用__attribute__
的enables the compiler to check the format string against the parameters provided机制声明它。