您的问题的问题在于它没有任何意义。 C++ 编译器将一些源代码翻译成二进制程序。要求是生成的程序必须根据 C++ 标准的规则保留代码的可观察效果。这段代码:
for (int i = 0; i < var.size(); i++);
只是没有任何可观察到的效果。此外,它不会与周围的代码进行任何交互,编译器可能会将其完全优化掉;也就是不生成对应的程序集。
为了使您的问题有意义,您需要指定循环内发生的情况。问题
for (int i = 0; i < var.size(); i++) { ... }
答案很大程度上取决于... 实际上是什么。我相信@MatteoItalia 提供了一个非常好的答案,只是会添加对我所做的一些实验的描述。考虑以下代码:
int g(std::vector<int>&, size_t);
int f(std::vector<int>& v) {
int res = 0;
for (size_t i = 0; i < v.size(); i++)
res += g(v, i);
return res;
}
首先,即使调用 var.size() 几乎 100% 肯定会内联启用优化,并且这种内联通常转化为两个指针的减法,这仍然会给循环带来一些开销。如果编译器不能证明向量大小被保留(这通常是非常困难甚至不可行的,例如在我们的例子中),那么你最终会得到不必要的 load 和 sub(可能还有shift)指令。使用 GCC 9.2、-O3 和 x64 生成的循环程序集是:
.L3:
mov rsi, rbx
mov rdi, rbp
add rbx, 1
call g(std::vector<int, std::allocator<int> >&, unsigned long)
add r12d, eax
mov rax, QWORD PTR [rbp+8] // loads a pointer
sub rax, QWORD PTR [rbp+0] // subtracts another poniter
sar rax, 2 // result * sizeof(int) => size()
cmp rbx, rax
jb .L3
如果我们重写代码如下:
int g(std::vector<int>&, size_t);
int f(std::vector<int>& v) {
int res = 0;
for (size_t i = 0, e = v.size(); i < e; i++)
res += g(v, i);
return res;
}
然后,生成的程序集更简单(因此更快):
.L3:
mov rsi, rbx
mov rdi, r13
add rbx, 1
call g(std::vector<int, std::allocator<int> >&, unsigned long)
add r12d, eax
cmp rbx, rbp
jne .L3
向量大小的值简单地保存在寄存器中 (rbp)。
我什至尝试了一个不同的版本,其中向量被标记为const:
int g(const std::vector<int>&, size_t);
int f(const std::vector<int>& v) {
int res = 0;
for (size_t i = 0; i < v.size(); i++)
res += g(v, i);
return res;
}
令人惊讶的是,即使v.size() 无法在此处更改,生成的程序集与第一种情况相同(附加了mov、sub 和sar 指令)。
现场演示是here。
另外,当我把循环改成:
for (size_t i = 0; i < v.size(); i++)
res += v[i];
然后,在汇编级别的循环内没有对v.size()(指针减法)的评估。 GCC 能够在这里“看到”循环体不会以任何方式改变大小。