我的理解是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个字符的字符串,如上面的输出所示。
8条答案
按热度按时间qacovj5a1#
C++23更新
现在我们终于有了
std::print
,可以直接使用std::format
进行输出:这结合了两种方法的优点。
原始答案
它正在编译,因为
printf
不是类型安全的,因为它使用C sense1中的变量参数。printf
没有std::string
的选项,只有一个C风格的字符串。使用其他东西来代替它所期望的肯定不会给予你想要的结果。它实际上是未定义的行为,所以任何事情都可能发生。由于您使用的是C++,因此最简单的解决方法是使用
std::cout
正常打印,因为std::string
通过运算符重载支持打印:如果出于某种原因,需要提取C样式字符串,可以使用
std::string
的c_str()
方法来获取以null结尾的const char *
。如果你想要一个类似
printf
的函数,但是类型安全,可以查看变量模板(C++11,从MSVC12开始所有主流编译器都支持)。你可以找到一个here的例子。我不知道在标准库中有这样的实现,但是在Boost中可能有,特别是boost::format
。[1]:这意味着你可以传递任意数量的参数,但是函数依赖于你告诉它这些参数的数量和类型。在
printf
的情况下,这意味着一个带有编码类型信息的字符串,如%d
表示int
。如果你在类型或数量上撒谎,函数没有标准的方法来知道,尽管有些编译器有能力在你说谎时进行检查并给予警告。bvjveswy2#
请不要使用
printf("%s", your_string.c_str());
使用
cout << your_string;
代替。简短、简单和类型安全。实际上,当你编写C时,你通常希望完全避免使用printf
--它是C遗留下来的,在C中很少需要或有用。至于为什么应该使用
cout
而不是printf
,原因有很多,下面是一些最明显的例子:1.如问题所示,
printf
不是类型安全的。如果传递的类型与转换说明符中给定的类型不同,printf
将尝试使用它在堆栈上找到的任何类型,就好像它是指定的类型一样,从而给出未定义的行为。有些编译器可以在某些情况下警告这一点,但有些编译器根本不能/根本不会,没有编译器可以在所有情况下警告这一点。printf
是不可扩展的。你只能传递基本类型给它。它所理解的转换说明符集是硬编码在它的实现中的,你无法添加更多的说明符。大多数写得很好的C++应该主要使用这些类型来实现面向要解决的问题的类型。1.这使得格式设置变得更加困难。举个明显的例子,当你打印数字供人们阅读时,你通常希望每隔几位插入千位分隔符。数字的确切数量和用作分隔符的字符会有所不同,但
cout
也涵盖了这一点。例如:无名的地方(“”)根据用户的配置选择区域设置。因此,在我的计算机上(配置为美式英语),打印输出为
123,456.78
。对于计算机配置为比如说德国,它会打印出123.456,78
这样的东西。如果有人把它配置成印度的,它将打印为1,23,456.78
(当然还有很多其他的)。使用printf
,我得到的结果只有一个:123456.78
。它是一致的,但对任何地方的每个人来说都是"错误的“。基本上,解决它的唯一方法是单独进行格式化,然后将结果作为字符串传递给printf
,因为printf
本身根本无法正确地完成这项工作。1.尽管
printf
字符串非常紧凑,但它们可能非常难以阅读,即使是在几乎每天都使用printf
的C程序员中,我猜至少99%的人需要查找内容以确定%#x
中的#
的含义,以及它与%#f
中的#
的含义有何不同(而且是的,它们的意思完全不同)。moiiocjp3#
如果你想要一个类似c的字符串(
const char*
)与printf一起使用,请使用myString.c_str()
谢谢
m2xkgtsf4#
使用std::printf和c_str()示例:
xdyibdwo5#
可以使用
snprinft
来确定所需的字符数并分配大小合适的缓冲区。这是对www.example.com上example的一个小改动cppreference.com
kx7yvsdv6#
printf
接受可变数量的参数。这些参数只能是Plain Old Data(POD)类型。向printf
传递POD以外的任何内容的代码只能编译,因为编译器假定您的格式正确。%s
意味着相应的参数应该是指向char
的指针。在您的情况下,它是std::string
,而不是const char*
。printf
不知道它,因为参数类型丢失了,应该从格式参数中恢复。当将std::string
参数转换为const char*
时,结果指针将指向内存中某个不相关的区域,而不是您想要的C字符串。因此,您的代码将打印出乱码。当
printf
是an excellent choice for printing out formatted text时,(特别是如果你打算有填充),始终启用警告因为这样的错误很容易避免。如果printf
系列可以以更快更漂亮的方式完成相同的任务,就没有理由使用笨拙的std::cout
机制。只要确保你已经启用了所有的警告(-Wall -Wextra
),你就可以了。如果你使用你自己定制的printf
实现,你应该用__attribute__
机制声明它。cotxawn77#
主要的原因可能是C字符串是一个包含当前长度值的结构体,而不仅仅是一个以0字节结束的字符序列的地址。Printf及其同类期望找到这样一个序列,而不是一个结构体,因此被C字符串搞混了。
就我个人而言,我相信printf有一个位置,C语法特性不能轻易地填补它,就像html中的表结构有一个位置,div不能轻易地填补它一样。正如Dykstra后来写的关于后藤的文章,他并不打算开创一个宗教,实际上只是反对把它作为一个杂牌来弥补设计糟糕的代码。
如果GNU项目能将printf家族添加到他们的g扩展中,那就太好了。
k97glaaz8#
如果大小很重要的话,Printf实际上非常好用。这意味着如果你正在运行一个内存有问题的程序,那么printf实际上是一个非常好的解决方案。Cout本质上是移动位来为字符串腾出空间,而printf只是接受一些参数并将其打印到屏幕上。如果你要编译一个简单的hello world程序,printf能够在不到60,000位的时间内编译它,而cout则需要超过100万位的时间来编译。
对于您的情况,我建议使用cout,因为它使用起来方便得多。尽管如此,我认为printf是一个值得了解的东西。