在循环条件下:为啥 strlen() 每次都被调用,而 vector.size() 只被调用一次? [复制]
Posted
技术标签:
【中文标题】在循环条件下:为啥 strlen() 每次都被调用,而 vector.size() 只被调用一次? [复制]【英文标题】:In loop condition: why is strlen() called every time, but vector.size() gets called only once? [duplicate]在循环条件下:为什么 strlen() 每次都被调用,而 vector.size() 只被调用一次? [复制] 【发布时间】:2018-10-28 14:07:29 【问题描述】:代码:
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
void f(const char* s)
for (size_t i = 0; i < strlen(s); i++)
printf("%c ", s[i]);
void g(const vector<int>& v)
for (size_t i = 0; i < v.size(); i++)
printf("%d ", v[i]);
编译链接:https://godbolt.org/z/PCi5yg
你会看到汇编代码:
在函数f()
中,strlen(s)
每次都会被调用;
但在函数 g()
中,v.size()
只被调用一次。
这是为什么呢?
参数为const,在内循环中没有改变参数。
【问题讨论】:
嗯,向量是const
,但strlen
不知道const,s
不是const。
可以在扫描C风格字符串的代码中使用for (size_t i = 0; s[i] != '\0'; i++)
,避免使用strlen()
。
请记住,strlen
必须访问字符串中的每个字符,直到找到终止的 nul 字符。如果 C-Style 字符串在循环内没有改变,这可能是非常低效的。 std::vector
可以将其大小存储在成员变量中,因此可以快速返回大小;与std::string
类似。
【参考方案1】:
您误解了生成的程序集。并不是假定向量的大小不变,只是对v.size()
的调用是内联的。向量的大小仍然在每次循环迭代时重新计算并加载到rax
。
const
限定符仅防止函数f
和g
修改它们引用的对象。这并不意味着他们可以假设对象是真正不可变的并且不能改变大小。编译器必须假设非内联函数可能会改变向量,包括printf
。所以函数必须重新计算向量的大小才能正确。
您可以进一步说服自己,即使经过优化,turning off inlining (-fno-inline
) 也会重新计算大小。生成的程序集非常清楚地展示了对vector::size
的调用。
【讨论】:
@Broman - 别担心。 West const 有时甚至会误导最好的人。 是的,你是对的!我误解了生成的程序集。正如您所说:Another thread of execution theoretically could be changing the vector
,所以编译器在大多数情况下不会缓存该值?我们应该在循环之前计算它吗?谢谢!
@HangYuan - 不能告诉你你应该做什么。这取决于您的应用程序及其需求。但我可以告诉您,只要您编写正确的程序(我的意思是正确的,没有未定义的行为等),符合标准的 c++ 实现将尽力产生正确的行为。
@StoryTeller 非常感谢!
理论上另一个执行线程可能正在改变向量。不,编译器确实根据 that 是 UB (MCU programming - C++ O2 optimization breaks while loop) 进行优化。但是对像printf
这样的非内联函数的调用可以改变任何东西,除了escape analysis 证明它不能的本地状态。如果循环中没有任何非内联函数调用,别名分析可能能够证明提升strlen
或v.size()
是安全的。以上是关于在循环条件下:为啥 strlen() 每次都被调用,而 vector.size() 只被调用一次? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
是否每次都为 Iterables 调用 for 循环的“条件”?