我的理解是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个字符的字符串,例如上面的输出。
答案 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
,原因很多。以下是一些最明显的例子:
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
的含义(是的,它们的意思完全不同)。答案 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字符串。因此,您的代码会打印出乱码。
虽然printf
是an 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__
机制声明它。