【问题标题】:how many times will strlen() be called in this for loop?在这个 for 循环中会调用多少次 strlen()?
【发布时间】:2011-01-04 04:52:36
【问题描述】:

下面的 strlen() 函数是否只被调用一次(存储的值以供进一步比较);还是每次进行比较时都会调用它?

for (i = 0; i < strlen(word); i++)
{ /* do stuff */ }

【问题讨论】:

    标签: c loops


    【解决方案1】:

    这取决于实现。通常,它每次都会被调用,但是,如果编译器可以看到 word 永远不会改变,并且 strlen 是一个纯函数(没有副作用),它可以取消调用。

    请参阅:http://underhanded.xcott.com/?page_id=15,了解一个众所周知的被利用示例。 :-)

    【讨论】:

    • @Chris:这个函数是一元的不一定有助于别名,也不会只将word 传递给采用char const * 的函数。如果word 是非限制性的,那么在某处可能有一个全局char*,被调用者修改,而这恰好是word 的别名。如果被调用者在不同的翻译单元中,并且没有以某种方式标记为纯,编译器几乎肯定不能排除这种情况,尽管整个程序优化可能会再试一次。基本上,如果循环体没有完全内联,那么不要屏住呼吸。
    • @Steve:+1 得分。我提到一元是因为我认为 restrict 参数仅相对于其他参数没有别名,并不是说所述参数不存在别名——在这种情况下,就像你说的那样,你仍然被塞满了——如果全局指针被保存在某处,程序分析。
    • 老实说,每次读C99标准中restrict的定义我都会头疼,还得看一会儿动画片让自己的大脑冷静下来;-)。但是,是的,我很确定不得在其范围内“通过”任何其他引用修改限制指针,无论该引用是否是另一个参数。因此,我认为即使在函数中(例如)通过函数指针回调用户定义的代码的函数中,restrict 也允许优化,此时再多的静态分析也无济于事。
    • @Thomas Matthews:每次都必须执行吗?请在需要此行为的标准中引用您的源代码,并阻止编译器执行分析以优化他们确定为常量表达式的内容。此外,将word 声明为const 实际上并不意味着它不能在循环内更改。
    • @Thomas:正如我在之前的评论中所说:“一个好的编译器实际上会查看代码以了解它的作用,而不仅仅是变量具有什么类型”。正如 jamesdlin 所说,编译器可以自行决定这样做,如果您在我的帖子中看到链接,一些编译器确实会执行它。
    【解决方案2】:

    我有时会将其编码为 ...

    for (int i = 0, n = strlen(word); i < n; ++i) { /* do stuff */ }
    

    ... 这样 strlen 只被调用一次(以提高性能)。

    【讨论】:

      【解决方案3】:

      将针对循环的每次迭代进行评估(编辑:如有必要)。

      就像 Tatu 说的,如果 word 的长度不会改变,你可以在 for 循环之前调用 strlen。但正如 Chris 所说,编译器可能足以意识到 word 无法更改,并自行消除重复调用。

      但是如果word 可以在循环期间改变长度,那么当然您需要将strlen 调用保持在循环条件中。

      【讨论】:

      • 实际上,只要word 没有在循环体中更改或声明为volatile,大多数编译器都应该对此进行优化;与往常一样,您可以检查(反)汇编,看看会发生什么......
      • 呸,这远不是完整的答案。如果编译器还算不错,它真的应该优化调用,所以它只评估一次。
      • @Noldorin:由于别名的原因,它需要的不仅仅是“一半体面的编译器”。如果循环通过指针修改函数中的 any 内存,或调用这样做或可能这样做的 any 代码,则编译器可能无法假定所讨论的指针没有指向与word 相同的内存。如果 word 在 C99 中用关键字 restrict 标记,那么它可以做出这样的假设,并且在极少数情况下它可能能够证明没有别名。但是如果没有看到循环体,就不能说这是一个简单的优化。
      • @PP:错误——编译器应该总是(并且通常)尽可能地优化。 @Steve:我想这是真的。我很确定 C# 会进行优化,我对此更熟悉,但 C 在某些方面确实会有所不同。
      • @Steve:这就是我的想法——为了让编译器消除每次迭代评估而不搞砸一些事情,需要考虑很多事情。
      【解决方案4】:

      strlen(word) 的执行次数取决于:

      1. 如果word被声明为常量(数据是常量)
      2. 或者编译器可以检测到 word 没有改变。

      举个例子:

      char word[256] = "Grow";
      
      for (i = 0; i < strlen(word); ++i)
      {
        strcat(word, "*");
      }
      

      在此示例中,变量 word 使用循环进行修改:
      0) “成长” -- 长度 == 4
      1) "增长*" -- 长度 == 5
      2) “成长**”——长度 == 6

      但是,编译器可以分解出strlen 调用,因此如果变量word 被声明为常量,则它会被调用一次:

      void my_function(const char * word)
      {
        for (i = 0; i < strlen(word); ++i)
        {
           printf("%d) %s\n", i, word);
        }
        return;
      }
      

      函数已声明变量word 是常量数据(实际上,一个指向常量数据的指针)。因此长度不会改变,因此编译器只能调用一次strlen

      如有疑问,您始终可以自己执行优化,在这种情况下,这可能会提供更易读的代码。

      【讨论】:

      • 一个const-qualified 指针只是保证指针不会通过该特定变量(不进行强制转换)更改,而不是数据本身是不可变的。
      • 确实,jamesdin 说的。通常的情况是,如果有任何函数调用,就无法证明char * 指向的数据没有改变!或者如果任何内存被修改,因为 char 指针可能指向那个...
      【解决方案5】:

      strlen 检查提供的字符串的长度。这意味着如果长度为 10。只要 i 低于 10,您的迭代就会继续进行。

      在那种情况下。 10 次。

      Read more about loops

      【讨论】:

      • -1 因为字符串可能在循环中被修改,因此strlen 可以被无限次调用(假设编译器没有缓存strlen() 的结果)。跨度>
      • 此外,示例中不能保证i 也不会被修改。
      • 这是一个拒绝投票的愚蠢理由。假设他没有篡改字符串。它更像是一个非常基本的解释,它首先看起来如何,然后是循环如何工作的参考。
      • 因此引用了循环指令。
      【解决方案6】:

      每次迭代都会调用它。下面的代码只调用一次strlen函数。

      for (i = 0, j = strlen(word); i < j i++)
      { /* do stuff */ }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-05
        • 2012-10-20
        • 1970-01-01
        • 2012-07-07
        • 1970-01-01
        相关资源
        最近更新 更多