我可以使用寄存器作为循环计数器吗?

Can I use a register as a loop counter?

由于函数的调用约定规定保留了哪些寄存器,寄存器可以用作循环计数器吗?

我一开始以为ecx寄存器是用来做循环计数器的,后来发现我用过的一个stdcall函数并没有保留ecx的值,我不这么认为。

是否有保证(至少通过大多数使用的调用约定)被保留的寄存器?

注意:我在使用堆栈变量作为循环计数器时没有问题,我只是想确保这是唯一的方法。

您可以使用任何通用寄存器,偶尔也可以使用其他寄存器作为循环计数器(当然不是堆栈指针☺)。

要么你用一个手动循环,即替换…

loop label

…与…

dec ebp
jnz label

… 无论如何都更快(因为 AMD(以及后来的英特尔,当他们赶上时,以 MHz 为单位)人为地减慢了 loop 指令,否则 Windows® 和一些 Turbo Pascal编译软件崩溃)。

或者您只是在两者之间保存计数器:

label:
    push ecx
    call func
    pop ecx
    loop label

两者都是标准策略。

Is there a register that is guaranteed (by mostly used calling conventions at least) to be preserved?

如果您的循环代码不会调用任何外部实体,您可以在自己的代码中选择任何自由寄存器。

如果您的循环代码将调用外部实体,其中唯一保证的合同是 ABI 和调用约定,那么您必须 save/restore 您的寄存器并选择寄存器 case-by-case。

引用 Agner Fog 的优秀论文Calling conventions for different C++ compilers and operating systems:

6 Register usage

The rules for register usage depend on the operating system, as shown in table 4. Scratch registers are registers that can be used for temporary storage without restrictions (also called caller-save or volatile registers). Callee-save registers are registers that you have to save before using them and restore after using them (also called non-volatile registers). You can rely on these registers having the same value after a call as before the call...

...

另请参阅: