我的理解是,字符串是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个字符的字符串,如上面的输出。


如果你想要一个类似c的字符串(const char*)用于printf,请使用myString.c_str()

谢谢


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

修复这个问题最简单的方法,因为你使用的是c++,通常用std::cout打印它,因为std::string通过操作符重载支持它:

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

如果出于某种原因,你需要提取c风格的字符串,你可以使用std::string的c_str()方法来获得一个以null结尾的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之前的所有主要编译器上都支持)。你可以在这里找到一个例子。据我所知,在标准库中还没有这样的实现,但在Boost中可能会有,特别是Boost::format。


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


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

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

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

As the question shows, printf isn't type-safe. If the type you pass differs from that given in the conversion specifier, printf will 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. printf isn'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. 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 cout has 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 printf I get exactly one result: 123456.78. It is consistent, but it's consistently wrong for 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 printf itself simply will not do the job correctly. Although they're quite compact, printf format strings can be quite unreadable. Even among C programmers who use printf virtually every day, I'd guess at least 99% would need to look things up to be sure what the # in %#x means, and how that differs from what the # in %#f means (and yes, they mean entirely different things).


主要原因可能是c++字符串是一个包含当前长度值的结构体,而不仅仅是以0字节结束的字符序列的地址。Printf及其相关函数希望找到这样的序列,而不是结构体,因此会被c++字符串弄糊涂。

就我个人而言,我相信printf有一个c++语法特性无法轻易填充的地方,就像html中的表结构有一个div无法轻易填充的地方一样。就像Dykstra后来写的goto一样,他并不打算开始一种宗教,实际上只是反对把它作为一个拼凑品来弥补设计糟糕的代码。

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


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

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

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.

对于您的情况,我建议使用cout,因为它使用起来要方便得多。尽管我认为printf是值得了解的东西。


printf accepts a variable number of arguments. Those can only have Plain Old Data (POD) types. Code that passes anything other than POD to printf only compiles because the compiler assumes you got your format right. %s means that the respective argument is supposed to be a pointer to a char. In your case it is an std::string not const char*. printf does not know it because the argument type goes lost and is supposed to be restored from the format parameter. When turning that std::string argument 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.

While printf is 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 warnings because then mistakes like this are easily avoidable. There is no reason to use the clumsy std::cout mechanism if the printf family 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 printf implementation you should declare it with the __attribute__ mechanism that enables the compiler to check the format string against the parameters provided.


您可以使用snprinft来确定所需的字符数量,并分配适当大小的缓冲区。

int length = std::snprintf(nullptr, 0, "There can only be %i\n", 1 );
char* str = new char[length+1]; // one more character for null terminator
std::snprintf( str, length + 1, "There can only be %i\n", 1 );
std::string cppstr( str );
delete[] str;

这是对cppreference.com上一个例子的一个小的改编