将 printf 与 C++ 迭代器一起使用

时间:2021-04-13 20:23:54

标签: c++ iterator

在这种情况下,当我将 std::printf 与迭代器一起使用时,我不清楚发生了什么:

#include <string>
#include <vector>
#include <list>
#include <cstdio>
int main() {
   std::string str{"hello, world"};
   std::printf("%s\n", str.begin()); // prints "hello, world"
   std::vector<char> vec(str.begin(), str.end());
   std::printf("%s\n", vec.begin()); // still same output
   std::list<char> lst(str.begin(), str.end());
   std::printf("%s\n", lst.begin()); // weird ascii outputs ???
}

我还使用了 实用程序来模拟 printf 的行为,它看起来即使使用 va_arg(ap, char*) 仍然有效,并且行为方式与上面的代码相同,我有几个问题。 它适用于连续的数组(使用 RAI 迭代器)吗?还是UB?为什么即使 va_arg 也会允许这样的事情,是否有一些隐式转换?

1 个答案:

答案 0 :(得分:3)

TL;DR:这都是无效的 C++,因为 std::printf 没有类型安全的 API。 将非 POD 对象的引用作为 std::printf 参数传递始终无效printf 具有 C API。

在所有情况下发生的都是未定义的行为:您不应该编写那样的代码。语言规范本身并没有告诉你期望什么。这样的代码是一个错误,将无法通过任何合理的代码审查。

对于 std::stringstd::vector 迭代器,它们碰巧具有相同的大小、值、布局和参数传递约定在您的平台上与 {{ 1}} const char * 期望,它们恰好分别包含字符串/向量开头的地址。 printfstd::string 对数据数组的布局相同,即元素是连续的,巧合的是 std::vector<char> 中的内存块在字符串末尾有一个零,所以这恰好工作。也许它只是因为您在调试模式下构建而有效:)

对于 std::vector 迭代器的情况,它可能碰巧包含指向列表数据块头部的指针,或者指向包裹在列表项中的 std::list 开头的指针,并且因此,您会得到无意义的输出,但不会导致无效的内存访问,因为等效指针指向某个已分配的内存区域,否则有效。当然,这是您平台上实现细节的结果。有些平台和构建选项会使代码崩溃。不,你甚至不能指望它会可靠地崩溃。这就是UB很糟糕的原因。

在你的平台上,std::string 可能是一个同样糟糕的错误,但碰巧“工作”,因为 std::printf("%s\n", *lst.begin()); 产生对 *lst.begin() 的引用,而这样的引用通常是像指针一样布置和传递。当然,这仍然是 UB,所以不要做任何事情!

相关问题