我的理解是string是std命名空间的成员,那么为什么会发生以下情况?
1 2 3 4 5 6 7 8 9 10 11 12 13
| #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个字符的字符串,例如在上面的输出中。
-
只是让您知道,很多人批评这本书。 我能理解,因为关于面向对象的编程没有很多,但是我认为它并不像人们所声称的那样糟糕。
-
!! 好吧,在我沿书前进的过程中牢记这一点是件好事。 我肯定这将不会是明年左右左右唯一读过的C ++书籍,所以我希望它不会造成太多损害:)
-
使用最高的编译器警告会回答您的问题-使用gcc进行编译时。 MSVC如何处理此问题-我不知道。
之所以进行编译是因为printf不是类型安全的,因为它在C sense1中使用了变量参数。 printf没有用于std::string的选项,只有C样式的字符串。使用其他东西代替期望的东西绝对不会给您想要的结果。这实际上是不确定的行为,因此任何事情都可能发生。
由于使用的是C ++,最简单的解决方法是使用std::cout正常打印,因为std::string通过操作符重载来支持:
1
| std::cout <<"Follow this command:" << myString; |
如果出于某种原因需要提取C样式的字符串,则可以使用std::string的c_str()方法来获取以空值终止的const char *。使用您的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #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以来所有主要编译器均支持该模板)。您可以在这里找到一个示例。我对标准库中的实现一无所知,但在Boost中可能有,特别是boost::format。
[1]:这意味着您可以传递任意数量的参数,但是该函数依赖于您告诉这些参数的数量和类型。在printf的情况下,这意味着字符串的编码类型信息如%d表示int。如果您撒谎是关于类型或数字,则该函数没有标准的知道方式,尽管有些编译器可以在撒谎时进行检查并发出警告。
-
没有提及对字符串使用cout吗?
-
@MooingDuck,好点。这是杰里斯的答案,但被人们接受,这是人们公认的答案,他们可能会在见其他人之前离开。我已经添加了该选项,以便成为第一个看到的解决方案,也是推荐的解决方案。
请不要使用printf("%s", your_string.c_str());
请使用cout << your_string;。简短,简单且类型安全。实际上,在编写C ++时,通常希望完全避免使用printf -这是C语言的遗留物,在C ++中很少需要或有用。
关于为什么应使用cout而不是printf的原因很多。以下是一些最明显的示例:
如问题所示,printf不是类型安全的。如果传递的类型与转换说明符中给出的类型不同,则printf将尝试使用在堆栈上找到的任何内容,就好像它是指定的类型一样,从而给出未定义的行为。有些编译器可以在某些情况下对此发出警告,但是有些编译器完全不能/根本不会,在任何情况下都不能。
printf是不可扩展的。您只能将原始类型传递给它。它理解的一组转换说明符在其实现中进行了硬编码,因此您无法添加更多/其他。大多数写得很好的C ++应该主要使用这些类型来实现面向要解决的问题的类型。
这使体面的格式化变得更加困难。举一个明显的例子,当您打印供人们阅读的数字时,您通常希望每隔几位插入数千个分隔符。数字的确切数量和用作分隔符的字符会有所不同,但是cout也涵盖了这些数字。例如:
1 2 3 4
| 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中的>表示(是的,它们表示完全不同的东西)。
-
当我尝试编译cout << myString << endl;时,出现以下错误:Error 1 error C2679: binary << : no operator found which takes a right-hand operand of type std::string (or there is no acceptable conversion)
-
@ TheDarkIn1978:您可能忘记了#include 。 VC ++的标头中有一些奇怪之处,可让您定义一个字符串,但不将其发送到cout,而不包含标头。
-
我确实忘记了(实际上不知道)!谢谢 :)
-
@Jerry:只想指出,在处理大数据时,使用printf比使用cout快得多。因此,请不要说这是没有用的:D
-
@Programmer:请参阅stackoverflow.com/questions/12044357/。简介:多数情况下cout较慢,这是因为您在不应使用的地方使用了std::endl。
-
当这些参数生效时,c中的printf的对应部分是什么? (我忘了名字,我的意思是那%一些字母的东西!)
-
@Hossein:据我所知,C ++中唯一采用printf样式格式字符串的函数是C ++从C"继承"的printf系列。
-
典型的C ++专家自大。如果确实存在printf,为什么不使用它呢?
-
@kuroineko:我已经编辑了答案以解释其原因。
-
好的,很抱歉发表评论。尽管如此,printf仍然非常便于调试,并且流虽然功能强大得多,但缺点是代码无法给出实际输出的任何信息。对于格式化输出,printf仍然是可行的选择,而且两个系统无法更好地协作也很可惜。当然只是我的意见。
-
printf是线程安全的(不是cout),并且使用printf进行格式化比使用流(cout,stringstream等)要好得多。
-
@oopscene:POSIX要求printf是线程安全的,但并非所有系统都完全符合POSIX(当然,有些甚至不尝试)。您对"表现更好"的含义还不够清楚,无法对此发表有意义的评论。最后,它归结为一件事:是的,至少在某些情况下,至少有一些合理的理由要支持使用printf,但是这种情况比某些人想像的要少得多,而且手头的案件甚至没有结案。
-
与printf相比,cout的主要缺点是cout不(和printf确实)提供基于格式字符串的格式设置。这种格式对于打印包含多个值的语句的语句的国际化至关重要,因为可能需要根据打印语句的人类读者的语言来更改值在语句中的顺序和位置。国际化涉及的不止此,但是使用printf和消息目录比使用cout可以更快地获得有用的东西。
-
@LouisStrous:我曾经一次同意(和多年前发布了类似的声明)。这允许您做某事,但是做得很差,以至于您最好根本不做某件事,以免有人将其误认为是已完成/可用的端口。它导致了所谓的" MS-DOS综合症"-太差了,它几乎无法使用,但是足以说服许多人认为它足够好,所以他们没有尝试对其进行改进。
-
@JerryCoffin有人麻烦写这篇文章:msdn.microsoft.com/en-us/magazine/dn913181.aspx显然,它有它的粉丝:-)
-
@Dilip:确实如此,而且在某种程度上,我什至在其中。当您完全需要精确格式化输出时(仅举一个明显的例子),我当然不喜欢iostream的冗长。然而,如果printf是答案,那么有人会问一个糟糕的问题。
-
@kuroineko对于您的笔记,我同意100%。典型...在我眼中,cout是C ++提供的最差功能。
-
@AlBundy:我同意cout(一般来说,iostream)有很多问题。即使这样,它们在很多方面都比printf更好。
-
否,在启用所有警告开关的情况下使用gcc时。没有什么比这更安全,尤其方便了:100%类型安全,检查参数计数。使用cout进行格式化令人恐惧-特别是对于长日志文件条目。当C ++专家提出cout <<"Hallo"或数字时,我总是在笑。但是要用30个参数来做[正如我所说的日志] ...
-
@AlBundy:不,它甚至不接近100%类型安全。仅当格式字符串是字符串文字时,类型检查才起作用。如果您甚至尝试专业地完成这项工作,那基本上就不会发生-总是从外部文件加载格式字符串以进行本地化。在此过程中,您将丢失所有类型检查。
-
类型检查仅在格式字符串是字符串文字时才有效是,这就是我要处理的内容。不同的情况->另一种解决方案。 gcc在编译时已经警告我。当我想到使用cout的解决方案和基本方法时,我会感到恶心。
-
@AlBundy:我建议您问一个有关如何做自己想完成的事情的问题。我想这取决于您愿意忍受什么的问题-正如我已经说过的(而且我不是在开玩笑),cout确实很糟糕。另一方面,printfs完全缺乏类型检查,这已经远远超出了令人难以接受的范围,成为犯罪分子的不可接受之举。
-
与该建议相反,以我的经验,大型的复杂C ++项目尽可能不使用甚至完全禁止c ++流,而更喜欢使用printf。这个建议对初学者确实有意义
如果希望将类似c的字符串(const char*)与printf一起使用,请使用myString.c_str()
谢谢
使用std :: printf和c_str()
例:
1
| std::printf("Follow this command: %s", myString.c_str()); |
如果尺寸很重要,Printf实际上非常好用。这意味着,如果您正在运行一个内存问题的程序,那么printf实际上是一个非常好的解决方案。 Cout实质上将位移开以为字符串留出空间,而printf只是接受某种参数并将其打印到屏幕上。如果要编译一个简单的hello world程序,则printf可以用不到60,000位(而不是cout)来编译它,而编译将花费一百万位以上。
对于您的情况,id建议仅使用cout,因为它使用起来更加方便。虽然,我认为printf是一个很好的认识。
printf接受可变数量的参数。那些只能具有普通旧数据(POD)类型。将POD以外的任何内容传递给printf的代码只能进行编译,因为编译器认为您的格式正确。 %s意味着相应的参数应该是指向char的指针。在您的情况下,它是std::string而不是const char*。 printf不知道它,因为参数类型丢失了,应该从format参数恢复。将那个std::string自变量转换为const char*时,结果指针将指向一些无关的内存区域,而不是所需的C字符串。因此,您的代码会打印出乱码。
尽管printf是打印格式化文本的绝佳选择,(特别是如果您打算使用填充),但是如果未启用编译器警告,则可能很危险。始终启用警告,因为这样的错误很容易避免。如果printf系列可以更快更漂亮的方式完成相同的任务,则没有理由使用笨拙的std::cout机制。只需确保已启用所有警告(-Wall -Wextra),一切都会好起来的。如果您使用自己的自定义printf实现,则应使用__attribute__机制对其进行声明,该机制使编译器可以根据提供的参数检查格式字符串。
主要原因可能是C ++字符串是一个包含当前长度值的结构,而不仅仅是一个以0字节终止的字符序列的地址。 Printf及其亲戚希望找到这样的序列,而不是结构,因此会被C ++字符串弄糊涂。
对于我自己来说,我相信printf的位置不容易被C ++语法功能填充,就像html中的表结构具有不容易被div填充的位置一样。正如Dykstra稍后写到的有关goto的文章一样,他无意开创一种宗教,实际上只是在争辩说要用它作为鞭打来弥补设计不良的代码。
如果GNU项目将printf系列添加到其g ++扩展中,那就太好了。