c++ 是否使用标准::字符串打印?

ds97pgxw  于 2023-02-17  发布在  其他
关注(0)|答案(8)|浏览(150)

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

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

qacovj5a

qacovj5a1#

C++23更新

现在我们终于有了std::print,可以直接使用std::format进行输出:

#include <print>
#include <string>

int main() {
    // ...
    std::print("Follow this command: {}", myString);
    // ...
}

这结合了两种方法的优点。

原始答案

它正在编译,因为printf不是类型安全的,因为它使用C sense1中的变量参数。printf没有std::string的选项,只有一个C风格的字符串。使用其他东西来代替它所期望的肯定不会给予你想要的结果。它实际上是未定义的行为,所以任何事情都可能发生。
由于您使用的是C++,因此最简单的解决方法是使用std::cout正常打印,因为std::string通过运算符重载支持打印:

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

如果出于某种原因,需要提取C样式字符串,可以使用std::stringc_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开始所有主流编译器都支持)。你可以找到一个here的例子。我不知道在标准库中有这样的实现,但是在Boost中可能有,特别是boost::format
[1]:这意味着你可以传递任意数量的参数,但是函数依赖于你告诉它这些参数的数量和类型。在printf的情况下,这意味着一个带有编码类型信息的字符串,如%d表示int。如果你在类型或数量上撒谎,函数没有标准的方法来知道,尽管有些编译器有能力在你说谎时进行检查并给予警告。

bvjveswy

bvjveswy2#

请不要使用printf("%s", your_string.c_str());
使用cout << your_string;代替。简短、简单和类型安全。实际上,当你编写C时,你通常希望完全避免使用printf--它是C遗留下来的,在C中很少需要或有用。
至于为什么应该使用cout而不是printf,原因有很多,下面是一些最明显的例子:
1.如问题所示,printf不是类型安全的。如果传递的类型与转换说明符中给定的类型不同,printf将尝试使用它在堆栈上找到的任何类型,就好像它是指定的类型一样,从而给出未定义的行为。有些编译器可以在某些情况下警告这一点,但有些编译器根本不能/根本不会,没有编译器可以在所有情况下警告这一点。

  1. printf是不可扩展的。你只能传递基本类型给它。它所理解的转换说明符集是硬编码在它的实现中的,你无法添加更多的说明符。大多数写得很好的C++应该主要使用这些类型来实现面向要解决的问题的类型。
    1.这使得格式设置变得更加困难。举个明显的例子,当你打印数字供人们阅读时,你通常希望每隔几位插入千位分隔符。数字的确切数量和用作分隔符的字符会有所不同,但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本身根本无法正确地完成这项工作。
1.尽管printf字符串非常紧凑,但它们可能非常难以阅读,即使是在几乎每天都使用printf的C程序员中,我猜至少99%的人需要查找内容以确定%#x中的#的含义,以及它与%#f中的#的含义有何不同(而且是的,它们的意思完全不同)。

moiiocjp

moiiocjp3#

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

m2xkgtsf

m2xkgtsf4#

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

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

xdyibdwo5#

可以使用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;

这是对www.example.com上example的一个小改动cppreference.com

kx7yvsdv

kx7yvsdv6#

printf接受可变数量的参数。这些参数只能是Plain Old Data(POD)类型。向printf传递POD以外的任何内容的代码只能编译,因为编译器假定您的格式正确。%s意味着相应的参数应该是指向char的指针。在您的情况下,它是std::string,而不是const char*printf不知道它,因为参数类型丢失了,应该从格式参数中恢复。当将std::string参数转换为const char*时,结果指针将指向内存中某个不相关的区域,而不是您想要的C字符串。因此,您的代码将打印出乱码。
printfan excellent choice for printing out formatted text时,(特别是如果你打算有填充),始终启用警告因为这样的错误很容易避免。如果printf系列可以以更快更漂亮的方式完成相同的任务,就没有理由使用笨拙的std::cout机制。只要确保你已经启用了所有的警告(-Wall -Wextra),你就可以了。如果你使用你自己定制的printf实现,你应该用__attribute__机制声明它。

cotxawn7

cotxawn77#

主要的原因可能是C字符串是一个包含当前长度值的结构体,而不仅仅是一个以0字节结束的字符序列的地址。Printf及其同类期望找到这样一个序列,而不是一个结构体,因此被C字符串搞混了。
就我个人而言,我相信printf有一个位置,C语法特性不能轻易地填补它,就像html中的表结构有一个位置,div不能轻易地填补它一样。正如Dykstra后来写的关于后藤的文章,他并不打算开创一个宗教,实际上只是反对把它作为一个杂牌来弥补设计糟糕的代码。
如果GNU项目能将printf家族添加到他们的g
扩展中,那就太好了。

k97glaaz

k97glaaz8#

如果大小很重要的话,Printf实际上非常好用。这意味着如果你正在运行一个内存有问题的程序,那么printf实际上是一个非常好的解决方案。Cout本质上是移动位来为字符串腾出空间,而printf只是接受一些参数并将其打印到屏幕上。如果你要编译一个简单的hello world程序,printf能够在不到60,000位的时间内编译它,而cout则需要超过100万位的时间来编译。
对于您的情况,我建议使用cout,因为它使用起来方便得多。尽管如此,我认为printf是一个值得了解的东西。

相关问题