【问题标题】:Should I bring temporary variable declarations out of loops in C and C++?我应该将临时变量声明带出 C 和 C++ 的循环吗?
【发布时间】:2010-07-28 13:27:52
【问题描述】:

这就是我的意思,假设我有这样的代码:

for (int i = 0; i < 1000; i++) {
    char* ptr = something;
    /*
    ... use ptr here
    */
}

好像char* ptr在循环中每次都被分配,导致无效?

这样写更有效吗?

char* ptr = something;
for (int i = 0; i < 1000; i++) {
    /*
    ... use ptr here
    */
}

请评论这个有趣的问题。谢谢!

谢谢,博达·赛多。

【问题讨论】:

  • 同意:这些天编译器优化了很多。但是将它放入你的循环有什么好处呢?
  • 你的“东西”是什么?这是一个 const char* 常量 c 字符串,这是一个变量,...?

标签: c loops temporary


【解决方案1】:

它可以产生性能差异,但如果合适的话,许多优化编译器会为您进行这种优化。这称为“loop-invariant code motion”。

【讨论】:

    【解决方案2】:

    我认为最好尽可能限制变量名的范围;如果ptr 不打算在循环外引用,则不应在循环外声明它。

    但是,如果 something 被证明是一个昂贵的操作 AND 它是不变的(即,它不依赖于 iAND 它会阻止你满足硬性能要求的代码,那么是的,您应该将声明移到循环之外。

    这很难说,但你可以这样做:

    do
    {
      char *ptr = something;
      for (int i = 0; i < 1000; i++)
      {
        /* use ptr here */
      }
    } while (0);
    

    您仍在限制ptr 的范围,但您不再在每次循环迭代时都分配它。

    【讨论】:

    • +1: 但顺便说一句,如果您自己编写代码,通常不需要大括号(或尾随分号)周围的 do/while 关键字排队。需要引入作用域的宏通常使用该构造,因此它们不会对各种其他语法位造成意外(例如在 if/else 语句中导致语法问题),但在宏之外通常是不必要的(尽管它不会造成伤害,但您可能会认为关键字不必要的混乱)。
    【解决方案3】:

    代码的翻译方式实际上取决于您的编译器及其执行的优化。编译器可能会执行“哑”翻译并分配每个循环,或者它可能会在优化阶段为您将分配放在循环之外。为了安全起见,我会将声明放在循环之外。与任何事情一样,您可以测试两者并查看每个循环需要多长时间才能看到是否存在差异。

    【讨论】:

    • 在我遇到的任何架构的声明点都没有进行“分配”。构造、初始化和销毁​​,是的。
    • “为了安全……”——不。这完全是错误的方式:误导性的伪优化。正确的方法是:在出现无效证明之前,使用语义上最有意义的形式(即将它放在循环中)。 只有如果进一步的知识表明这是不够的,采取行动。
    • 在我看来,如果一个变量对于循环是不变的,那么语义上最有意义的形式是在循环之外声明它,从而表明它不依赖于循环体。
    • 我看不出将声明放在循环之外有什么意义。是的,我可以理解该变量可以在循环的深处使用,但关键是您不提前知道优化,除非您之前已经分析过该类型的代码或者您对编译器本身有深入的了解。所以是的,我坚持我的“为了安全”作为一个有效的论点。
    【解决方案4】:

    您必须自己分析和查看。我的Check out this question(和公认的答案)作为参考。正如 jalf 所说,期望它被优化掉只是一个经验法则(对于 POD 类型它可能是正确的)但是您需要通过分析来支持它。

    【讨论】:

      【解决方案5】:

      作为“C”程序中的个人约定,我喜欢将变量声明放在函数顶部、文件顶部或通用 .h 文件中。如果我开始将声明隐藏在代码中,则可能会造成混淆并且容易忘记变量范围,从而导致不良后果。

      【讨论】:

      • 这不仅仅是个人约定的问题,C89/C90 要求在其作用域的顶部声明局部变量。
      • 有 SE 参数认为变量声明应尽可能接近其用途,以便在代码中明确变量的使用范围。
      • 同意,我的“个人约定”是我喜欢将声明专门放在这三个区域中,并尽量避免将声明放在 for/while 循环中。我尝试将在函数级别使用“最内层”声明作为一种实践(除非有真正的充分理由)。重构有助于使这种个人需求更易于管理。
      • 我不同意。变量应在最小的封闭范围内声明,以尽可能增加局部性并限制范围。如果您的作用域足够大以至于这变得令人困惑,那么其他事情可能也会令人困惑,您应该考虑重构。
      • 我只使用这 3 个点作为指导。我确实提到通过重构功能应该很小并且只做一件事。这通常会导致最小的封闭范围(总是有例外,但我将此作为经验法则)。
      【解决方案6】:

      对于示例中的char* 等内置类型,没有太大区别。第二种形式在循环退出后可用,并且只计算一次初始化程序。但正如 John 所说,无论如何,大多数编译器都会预先计算初始化。

      如果ptr 在循环内被重新分配(如果你不重新分配它,你应该把它改成char* const ptr)有一个明显的区别,因为该值将保留在前一次迭代中,而不是重置。

      最后,非 POD 类型的构造函数和析构函数将在每次循环迭代时运行。

      【讨论】:

        猜你喜欢
        • 2011-12-23
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 2016-02-23
        • 2022-12-05
        • 1970-01-01
        • 2019-06-08
        • 1970-01-01
        相关资源
        最近更新 更多