简体   繁体   English

使用 printf 和 C++ 迭代器

[英]using printf with C++ iterators

I'm not clearly understanding what's happening when I use std::printf with an iterator in this context:在这种情况下,当我使用带有迭代器的 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 ???
}

I also used <stdarg.h> utilities to simulate printf's behavior and it looks like it still works even with va_arg(ap, char*) and behaves the same way as the code above, and I got couple of questions.我还使用 <stdarg.h> 实用程序来模拟 printf 的行为,它看起来即使使用va_arg(ap, char*)仍然可以工作,并且行为方式与上面的代码相同,我有几个问题。 Does it work on arrays that are contiguous (with a RAI iterator)?它是否适用于连续的 arrays(使用 RAI 迭代器)? or it's UB?还是UB? Why even va_arg will allow such thing, is there some implicit conversions?为什么连va_arg都会允许这样的事情,是否有一些隐式转换?

TL;DR: It's all invalid C++ , since std::printf doesn't have a type safe API. TL;DR:这都是无效的 C++ ,因为std::printf没有类型安全的 API。 Passing references to non-POD objects as std::printf arguments is always invalid : printf has a C API. Passing references to non-POD objects as std::printf arguments is always invalid : printf has a C API.

What's happening in all cases is undefined behavior: you should not be writing code like that.在所有情况下发生的都是未定义的行为:您不应该编写这样的代码。 The language spec itself tells you nothing about what to expect.语言规范本身并没有告诉您期望什么。 Such code is a bug and would fail any sensible code review.这样的代码是一个错误,并且会通过任何合理的代码审查。

For the case of std::string and std::vector iterators, they happen to have the same size, value, layout, and parameter passing convention on your platform as a const char * that printf expects, and they happen to contain addresses of the beginning of the string/vector, respectively.对于std::stringstd::vector迭代器,它们恰好在您的平台上具有与printf期望的const char *相同的大小、值、布局和参数传递约定,并且它们恰好包含字符串/向量的开头,分别。 Both std::string and std::vector<char> lay out the data array identically, ie the elements are contiguous, and coincidentially the memory block in std::vector has a zero at the end of the string, so this happens to work. std::stringstd::vector<char>都以相同的方式布置数据数组,即元素是连续的,巧合的是, std::vector中的 memory 块在字符串的末尾有一个零,所以这发生在工作。 Maybe it only works because you build in debug mode:)也许它只是因为你在调试模式下构建才有效:)

For the case of std::list iterator, it probably happens to contain a pointer to either the header of the list's data block, or a pointer to the beginning of std::string wrapped in the list item, and thus you get nonsense output that nevertheless doesn't cause an invalid memory access, since the equivalent pointer points to some allocated memory area and is otherwise valid.对于std::list迭代器,它可能恰好包含指向列表数据块的 header 的指针,或指向包含在列表项中的std::string开头的指针,因此您会得到无意义的 output尽管如此,这不会导致无效的 memory 访问,因为等效指针指向某些已分配的 memory 区域,否则是有效的。 This is a consequence of implementation details on your platform, of course.当然,这是您平台上实施细节的结果。 There are platforms and build options where that code will just crash.在某些平台和构建选项中,代码只会崩溃。 And no, you can't even depend that it will crash dependably.不,你甚至不能相信它会可靠地崩溃。 That's why UB is pretty bad.这就是为什么UB非常糟糕的原因。

On your platform, std::printf("%s\n", *lst.begin());在您的平台上, std::printf("%s\n", *lst.begin()); would probably be just as bad of a bug yet happen to "work", since *lst.begin() produces a reference to std::string , and such a reference usually is laid out and passed around like a pointer would be.可能与“工作”一样糟糕的错误,因为*lst.begin()产生对std::string的引用,并且这种引用通常像指针一样被布置和传递。 This is still UB of course, so don't do any of it!当然,这仍然是 UB,所以不要做任何事情!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM