C++ printf 与 std::string?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10865957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:33:29  来源:igfitidea点击:

printf with std::string?

c++stringnamespacesprintfstd

提问by TheDarkIn1978

My understanding is that stringis a member of the stdnamespace, so why does the following occur?

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

enter image description here

在此处输入图片说明

Each time the program runs, myStringprints a seemingly random string of 3 characters, such as in the output above.

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

回答by chris

It's compiling because printfisn't type safe, since it uses variable arguments in the C sense1. printfhas no option for std::string, only a C-style string. Using something else in place of what it expects definitely won't give you the results you want. It's actually undefined behaviour, so anything at all could happen.

它正在编译,因为printf它不是类型安全的,因为它使用 C 意义上的变量参数1printf没有选项std::string,只有一个 C 风格的字符串。使用其他东西代替它所期望的肯定不会给你想要的结果。这实际上是未定义的行为,所以任何事情都可能发生。

The easiest way to fix this, since you're using C++, is printing it normally with std::cout, since std::stringsupports that through operator overloading:

解决此问题的最简单方法,因为您使用的是 C++,所以通常使用 打印它std::cout,因为std::string通过运算符重载支持它:

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

If, for some reason, you need to extract the C-style string, you can use the c_str()method of std::stringto get a const char *that is null-terminated. Using your example:

如果由于某种原因需要提取 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;
}

If you want a function that is like printf, but type safe, look into variadic templates (C++11, supported on all major compilers as of MSVC12). You can find an example of one here. There's nothing I know of implemented like that in the standard library, but there might be in Boost, specifically boost::format.

如果您想要一个类似于printf,但类型安全的函数,请查看可变参数模板(C++11,自 MSVC12 起,所有主要编译器都支持)。你可以在这里找到一个例子。我不知道在标准库中实现了什么,但在 Boost 中可能有,特别是boost::format.



[1]: This means that you can pass any number of arguments, but the function relies on you to tell it the number and types of those arguments. In the case of printf, that means a string with encoded type information like %dmeaning int. If you lie about the type or number, the function has no standard way of knowing, although some compilers have the ability to check and give warnings when you lie.

[1]:这意味着您可以传递任意数量的参数,但该函数依赖于您告诉它这些参数的数量和类型。在 的情况下printf,这意味着具有编码类型信息的字符串,如%d含义int。如果你在类型或数字上撒谎,函数没有标准的知道方式,尽管一些编译器有能力在你撒谎时检查并给出警告。

回答by Jerry Coffin

Please don't use printf("%s", your_string.c_str());

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

Use cout << your_string;instead. Short, simple and typesafe. In fact, when you're writing C++, you generally want to avoid printfentirely -- it's a leftover from C that's rarely needed or useful in C++.

使用cout << your_string;来代替。简短、简单且类型安全。事实上,当您编写 C++ 时,您通常希望printf完全避免——它是 C 的剩余部分,在 C++ 中很少需要或有用。

As to whyyou should use coutinstead of printf, the reasons are numerous. Here's a sampling of a few of the most obvious:

至于为什么应该使用cout而不是printf,原因有很多。以下是一些最明显的示例:

  1. As the question shows, printfisn't type-safe. If the type you pass differs from that given in the conversion specifier, printfwill try to use whatever it finds on the stack as if it were the specified type, giving undefined behavior. Some compilers can warn about this under some circumstances, but some compilers can't/won't at all, and none can under all circumstances.
  2. printfisn't extensible. You can only pass primitive types to it. The set of conversion specifiers it understands is hard-coded in its implementation, and there's no way for you to add more/others. Most well-written C++ should use these types primarily to implement types oriented toward the problem being solved.
  3. It makes decent formatting much more difficult. For an obvious example, when you're printing numbers for people to read, you typically want to insert thousands separators every few digits. The exact number of digits and the characters used as separators varies, but couthas that covered as well. For example:

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

    The nameless locale (the "") picks a locale based on the user's configuration. Therefore, on my machine (configured for US English) this prints out as 123,456.78. For somebody who has their computer configured for (say) Germany, it would print out something like 123.456,78. For somebody with it configured for India, it would print out as 1,23,456.78(and of course there are many others). With printfI get exactly one result: 123456.78. It is consistent, but it's consistently wrongfor everybody everywhere. Essentially the only way to work around it is to do the formatting separately, then pass the result as a string to printf, because printfitself simply will notdo the job correctly.

  4. Although they're quite compact, printfformat strings can be quite unreadable. Even among C programmers who use printfvirtually every day, I'd guess at least 99% would need to look things up to be sure what the #in %#xmeans, and how that differs from what the #in %#fmeans (and yes, they mean entirely different things).
  1. 正如问题所示,printf不是类型安全的。如果您传递的类型与转换说明符中给出的类型不同,printf将尝试使用它在堆栈中找到的任何内容,就好像它是指定的类型一样,给出未定义的行为。有些编译器在某些情况下可以警告这一点,但有些编译器不能/不会,在所有情况下都不能。
  2. printf不可扩展。您只能将原始类型传递给它。它理解的一组转换说明符在其实现中是硬编码的,您无法添加更多/其他。大多数编写良好的 C++ 应该主要使用这些类型来实现面向要解决的问题的类型。
  3. 它使体面的格式化变得更加困难。举一个明显的例子,当您打印数字供人们阅读时,您通常希望每隔几位插入千位分隔符。数字的确切数量和用作分隔符的字符各不相同,但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它本身根本无法正确完成工作。

  4. 尽管它们非常紧凑,但printf格式字符串可能非常不可读。即使在printf几乎每天都使用的 C 程序员中,我想至少有 99% 的人需要查找东西来确定#in 的%#x含义,以及这与#in 的%#f含义有何不同(是的,它们的含义完全不同) )。

回答by Alessandro Pezzato

use myString.c_str()if you want a c-like string (const char*) to use with printf

使用myString.c_str(),如果你想有一个类似C的字符串(const char*)与printf的使用

thanks

谢谢

回答by Adel Ben Hamadi

Use std::printfand c_str() example:

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

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

回答by howard howard

Printf is actually pretty good to use if size matters. Meaning if you are running a program where memory is an issue, then printf is actually a very good and under rater solution. Cout essentially shifts bits over to make room for the string, while printf just takes in some sort of parameters and prints it to the screen. If you were to compile a simple hello world program, printf would be able to compile it in less than 60, 000 bits as opposed to cout, it would take over 1 million bits to compile.

如果大小很重要,Printf 实际上非常好用。意思是,如果您正在运行一个内存有问题的程序,那么 printf 实际上是一个非常好的评估器解决方案。Cout 本质上是移动位来为字符串腾出空间,而 printf 只是接收某种参数并将其打印到屏幕上。如果您要编译一个简单的 hello world 程序, printf 将能够以少于 60, 000 位的方式编译它,而 cout 则需要超过 100 万位才能编译。

For your situation, id suggest using cout simply because it is much more convenient to use. Although, I would argue that printf is something good to know.

对于您的情况,id 建议使用 cout 只是因为它使用起来方便得多。虽然,我认为 printf 是值得知道的。

回答by Hyena

printfaccepts a variable number of arguments. Those can only have Plain Old Data (POD) types. Code that passes anything other than POD to printfonly compiles because the compiler assumes you got your format right. %smeans that the respective argument is supposed to be a pointer to a char. In your case it is an std::stringnot const char*. printfdoes not know it because the argument type goes lost and is supposed to be restored from the format parameter. When turning that std::stringargument into const char*the resulting pointer will point to some irrelevant region of memory instead of your desired C string. For that reason your code prints out gibberish.

printf接受可变数量的参数。那些只能具有普通旧数据 (POD) 类型。传递 POD 以外的任何内容的代码printf只会编译,因为编译器假定您的格式正确。%s意味着相应的参数应该是指向 a 的指针char。在您的情况下,它std::string不是const char*printf不知道,因为参数类型丢失了,应该从格式参数中恢复。当将该std::string参数转换const char*为结果指针时,将指向一些不相关的内存区域,而不是您想要的 C 字符串。因此,您的代码会打印出乱码。

While printfis an excellent choice for printing out formatted text, (especially if you intend to have padding), it can be dangerous if you haven't enabled compiler warnings. Always enable warningsbecause then mistakes like this are easily avoidable. There is no reason to use the clumsy std::coutmechanism if the printffamily can do the same task in a much faster and prettier way. Just make sure you have enabled all warnings (-Wall -Wextra) and you will be good. In case you use your own custom printfimplementation you should declare it with the __attribute__mechanism that enables the compiler to check the format string against the parameters provided.

虽然printf打印格式化文本的绝佳选择(特别是如果您打算使用填充),但如果您尚未启用编译器警告,则可能会很危险。始终启用警告,因为这样的错误很容易避免。std::cout如果printf家庭能够以更快、更漂亮的方式完成相同的任务,就没有理由使用笨拙的机制。只要确保您已启用所有警告 ( -Wall -Wextra) 就可以了。如果您使用自己的自定义printf实现,则应使用使编译器能够根据提供的参数检查格式字符串__attribute__机制来声明它。

回答by MMacD

The main reason is probably that a C++ string is a struct that includes a current-length value, not just the address of a sequence of chars terminated by a 0 byte. Printf and its relatives expect to find such a sequence, not a struct, and therefore get confused by C++ strings.

主要原因可能是 C++ 字符串是一个包含当前长度值的结构,而不仅仅是以 0 字节结尾的字符序列的地址。Printf 及其亲属希望找到这样的序列,而不是结构体,因此会被 C++ 字符串混淆。

Speaking for myself, I believe that printf has a place that can't easily be filled by C++ syntactic features, just as table structures in html have a place that can't easily be filled by divs. As Dykstra wrote later about the goto, he didn't intend to start a religion and was really only arguing against using it as a kludge to make up for poorly-designed code.

就我个人而言,我认为 printf 有一个地方不能被 C++ 的语法特性轻易填充,就像 html 中的表结构有一个不能被 div 轻易填充的地方一样。正如 Dykstra 后来写到的关于 goto 的文章,他并不打算创立一种宗教,而且实际上只是反对将其用作弥补设计不佳代码的杂物。

It would be quite nice if the GNU project would add the printf family to their g++ extensions.

如果 GNU 项目将 printf 系列添加到他们的 g++ 扩展中,那就太好了。