首页 > 解决方案 > 在 C/C++ 中重置/环绕变量(ringBuffer 指针)

问题描述

我正在编写一些环形缓冲区,这个问题来了好几次。

假设我们有一个计数器,我们需要在一定计数后重置。我见过几个环形缓冲区的例子(主要是音频,环绕 r/w 指针),它们这样做:

x++;
if (x  == SOME_NUMBER ){ // Reseting counter
    x -= x;
}

这样做有什么区别/偏好:

x++;
if (x  == SOME_NUMBER ){ // Reseting counter
    x = 0;
}

?

这个问题适用于几乎所有类型的变量重置。在我的情况下,除了环形缓冲区,我还重置了一个做平均的计数器,所以在我做了所有的措施之后,我重置了那个计数器。

除了结果可能相同(x 重置为零)这一事实之外,一种方法与另一种方法之间可能存在一些差异。有什么偏好吗?

标签: c++c

解决方案


考虑你的片段的那些稍微修改过的版本

void f(int n)
{
    int x = 0;
    for (;;)
    {
        ++x;
        if (x == n ) {   // Reseting counter
            x -= x;
        }
        // Ending condition to avoid UB
        if ( x == 42 )
            return;
    }
}

void g(int n)
{
    int x = 0;
    for (;;)
    {
        ++x;
        if (x == n ) {
            x = 0;
        }

        if ( x == 42 )
            return;
    }
}

如果您查看生成的程序集(例如使用Compiler Explorer),您会注意到现代优化编译器如何利用as-if 规则

Clang (with -O2) 为这两个函数生成相同的机器代码。它用

xor     eax, eax

将零加载到寄存器中,然后

cmove   ecx, eax

在需要时“重置”另一个寄存器。

Gcc 只是创建f()然后g()变成

jmp     f(int)

那说

有什么偏好吗?

一个共同的指导方针是编写更具可读性和可维护性的代码,并仅在对其进行分析后探索可能的优化。

在大多数情况下,我会使用该x = 0;版本,因为它更好地传达了意图,恕我直言。我只能想到几个原因来采用x -= x;一个:

  • 它不依赖于“幻数”。但是,42我的片段中的文字就是这种情况,0是一个例外情况。
  • 它不需要任何隐式转换。考虑任何x不是int.
  • 可能有一些架构/工具链实际上可以提供更快的代码。我想不出来,但这无关紧要。

推荐阅读