提问人:Chunky Chunk 提问时间:6/3/2012 最后编辑:Micha WiedenmannChunky Chunk 更新时间:2/19/2023 访问量:519764
printf 与 std::string?
printf with std::string?
问:
我的理解是,它是命名空间的成员,那么为什么会出现以下情况呢?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;
}
每次程序运行时,都会打印一个看似随机的 3 个字符字符串,如上面的输出所示。myString
答:
如果您希望将类似 C 的字符串 () 与 printf 一起使用,请使用myString.c_str()
const char*
谢谢
C++ 23 更新
我们现在终于有一种直接用于输出的方法:std::print
std::format
#include <print>
#include <string>
int main() {
// ...
std::print("Follow this command: {}", myString);
// ...
}
这结合了两种方法的优点。
原始答案
它之所以进行编译,是因为它不是类型安全的,因为它使用 C 意义上的变量参数1。 没有 的选项,只有一个 C 样式的字符串。使用其他东西来代替它所期望的东西绝对不会给你带来你想要的结果。这实际上是未定义的行为,所以任何事情都可能发生。printf
printf
std::string
由于您使用的是 C++,因此解决此问题的最简单方法是使用 ,因为通过运算符重载支持它:std::cout
std::string
std::cout << "Follow this command: " << myString;
如果出于某种原因需要提取 C 样式的字符串,则可以使用 的方法来获取以 null 结尾的字符串。使用您的示例: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;
}
如果您想要一个类似于 但类型安全的函数,请查看可变参数模板(C++11,自 MSVC12 起在所有主要编译器上都支持)。你可以在这里找到一个例子。据我所知,在标准库中没有这样的实现,但在 Boost 中可能有,特别是 boost::format
。printf
[1]:这意味着你可以传递任意数量的参数,但函数依赖于你来告诉它这些参数的数量和类型。在 的情况下,这意味着一个字符串,其编码类型信息如 meaning。如果你在类型或数字上撒谎,该函数没有标准的知道方式,尽管一些编译器能够在你撒谎时进行检查并发出警告。printf
%d
int
评论
stdout
std::format
cout << "..." << s;
cout << format("... {}", s);
请不要使用printf("%s", your_string.c_str());
请改用。短小精悍,类型安全。事实上,当你编写C++时,你通常希望完全避免 - 它是C的遗留物,在C++中很少需要或有用。cout << your_string;
printf
至于为什么要用而不是,原因很多。以下是一些最明显的示例:cout
printf
- 正如问题所示,不是类型安全的。如果您传递的类型与转换说明符中给出的类型不同,则将尝试使用它在堆栈上找到的任何内容,就好像它是指定的类型一样,从而提供未定义的行为。在某些情况下,一些编译器可以对此发出警告,但有些编译器根本不能/根本不会,而且没有一个编译器在任何情况下都能做到。
printf
printf
printf
不可扩展。您只能将基元类型传递给它。它理解的一组转化说明符在其实现中是硬编码的,您无法添加更多/其他说明符。大多数编写良好的 C++ 应该主要使用这些类型来实现面向正在解决的问题的类型。它使体面的格式变得更加困难。举个明显的例子,当你打印数字供人们阅读时,你通常希望每隔几位数字插入数千个分隔符。确切的位数和用作分隔符的字符各不相同,但也包括了这一点。例如:
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
- 尽管它们非常紧凑,但格式字符串可能非常难以读取。即使在几乎每天都在使用的 C 程序员中,我猜至少有 99% 的人需要查找一些东西来确定 in 的含义,以及它与 in 的含义有何不同(是的,它们的含义完全不同)。
printf
printf
#
%#x
#
%#f
评论
#include <string>
cout
<string>
cout
std::endl
cout << "The" << color << "house"
主要原因可能是 C++ 字符串是一个包含当前长度值的结构,而不仅仅是以 0 字节结尾的字符序列的地址。Printf 及其亲戚希望找到这样的序列,而不是结构,因此被 C++ 字符串混淆。
就我自己而言,我相信 printf 有一个不容易被 C++ 语法特征填补的地方,就像 html 中的表格结构有一个不容易被 div 填补的地方一样。正如 Dykstra 后来写到的关于 goto 的文章,他并不打算建立一种宗教,实际上只是反对将其用作弥补设计不佳的代码的障碍。
如果 GNU 项目将 printf 系列添加到他们的 g++ 扩展中,那就太好了。
使用 std::p rintf 和 c_str() 例:
std::printf("Follow this command: %s", myString.c_str());
评论
c_str()
std::printf()
james-kanze
如果尺寸很重要,Printf 实际上很好用。这意味着,如果你正在运行一个内存有问题的程序,那么 printf 实际上是一个非常好的、低于评分者的解决方案。Cout 本质上是移动位以为字符串腾出空间,而 printf 只是接受某种参数并将其打印到屏幕上。如果你要编译一个简单的hello world程序,printf将能够在不到60, 000位的时间内编译它,而cout则需要超过100万位才能编译。
对于您的情况,id 建议使用 cout,因为它使用起来更方便。虽然,我认为 printf 是值得了解的。
printf
接受可变数量的参数。这些只能具有普通旧数据 (POD) 类型。将 POD 以外的任何内容传递给的代码只会编译,因为编译器假定您的格式正确。 表示相应的参数应该是指向 .就您而言,它不是. 不知道它,因为参数类型丢失了,应该从格式参数恢复。当将该参数转换为生成的指针时,指针将指向某个不相关的内存区域,而不是您想要的 C 字符串。出于这个原因,您的代码会打印出乱码。printf
%s
char
std::string
const char*
printf
std::string
const char*
虽然它是打印格式化文本的绝佳选择(特别是如果您打算进行填充),但如果您尚未启用编译器警告,它可能会很危险。始终启用警告,因为这样很容易避免这样的错误。如果家庭可以以更快、更漂亮的方式完成相同的任务,就没有理由使用笨拙的机制。只要确保你已经启用了所有警告(),你就会很好。如果您使用自己的自定义实现,则应使用使编译器能够根据提供的参数检查格式字符串的机制来声明它。printf
std::cout
printf
-Wall -Wextra
printf
__attribute__
您可以使用它来确定所需的字符数并分配适当大小的缓冲区。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 示例的一个小改编
评论
这是一种通用的方法。
#include <string>
#include <stdio.h>
auto print_helper(auto const & t){
return t;
}
auto print_helper(std::string const & s){
return s.c_str();
}
std::string four(){
return "four";
}
template<class ... Args>
void print(char const * fmt, Args&& ...args){
printf(fmt, print_helper(args) ...);
}
int main(){
std::string one {"one"};
char const * three = "three";
print("%c %d %s %s, %s five", 'c', 3+4, one + " two", three, four());
}
评论