【问题标题】:Basic C loops comparison基本 C 循环比较
【发布时间】:2013-09-06 00:30:52
【问题描述】:

给定

int x[10];
int y[10];
int n = 10;

版本 1

int i = 0;
while (i < n)
  y[i] = x[i++];

第 2 版

for (int i = 0; i < n; i++)
  y[i] = x[i]

这两个版本总是相同的吗?如果不是,它们什么时候不等价?

【问题讨论】:

    标签: c arrays for-loop while-loop


    【解决方案1】:

    这一行:

    y[i] = x[i++];
    

    未定义的行为。您不能在同一语句中使用ii++

    您的版本 2,在 for 控制语句中包含 i++,没问题。

    【讨论】:

    • 为什么第一个版本是未定义的行为,如果机器从左到右读取,什么时候没有意义?请看@moshe-beeri 的回答?
    • @Juto:也许您的编译器使用您正在使用的当前选项设置编译该语句,就好像它是从左到右读取一样。但是,语言标准并不能保证这一点,不同的编译器甚至编译器上的不同设置都可能生成不同的代码,但不会产生相同的结果。
    • @Juto 为什么第一个版本是未定义的行为? 因为标准是这样说的,而且它有一个很好的理由:举个例子 -- y =4; y = n++ + n++; if一个是遵循优先操作,y的值可能是9,但它甚至可能是8。这就是 C 定义序列点并保证所有side effects 将在继续下一条语句之前发生的原因,即在序列点之后。如果您想在两个序列点之间更改两次值,则不能保证哪个将首先评估,因此这是非法的。
    • @GregHewgill 虽然这是一个很好的答案,并且至少有 1000 篇关于此的帖子,但我仍然建议稍微解释一下,只是对为什么它的未定义行为稍作解释。即使是这些答案的一两个链接也是一个好主意......不过+1 :)
    【解决方案2】:

    如果我们围绕Greg Hewgill 在他的answer 中正确诊断的未定义行为进行编码,那么我们最终可能会得到如下代码:

    int x[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
    int y[10];
    int n = 10;
    
    i = 0;
    while (i < n)
    {
        if (i % 3 == 0)
            continue;
        y[i] = x[i];
        i++;
    //cont: ;
    }
    
    for (i = 0; i < n; i++)
    {
        if (i % 3 == 0)
            continue;
        y[i] = x[i];
    }
    

    由于continue 语句,这些循环是不等价的——事实上,第一个循环是无限循环。并且循环体可以在没有continue 的情况下编写,但我想要一些简单的东西来说明continuewhile 循环和for 循环中的行为。

    for 循环运行正常,不会初始化元素 y[0]y[3]y[6]y[9],但在其他方面运行良好。

    while 循环看起来很相似,但continue; 语句等效于goto cont;,其中cont 是被注释掉的标签,紧挨着右大括号之前。请注意,它会跳过i 的增量,这就是循环“无限”的原因。

    所以,如果里面没有continue,这两个循环是等价的。

    注意相关循环:

    for (int i = 0; i < n; i++)
    {
        if (i % 3 == 0)
            continue;
        y[i] = x[i];
    }
    

    与第一个 for 循环并不完全相同。变量iwhile 循环和第一个for 循环中的循环外部(之后)可用;当变量在for 循环本身中声明时,它不可用。

    【讨论】:

    • 我不确定你为什么在两行之间添加了continue 块,在问题中,continue 不存在?
    • 更正continue 不在问题中。我添加它是为了解释for 循环和while 循环不同的一种情况——当代码不调用未定义的行为时。在其他情况下,如果您避免未定义的行为,则两个循环是“相同的”,正如我也说过的。因此,除非您有 continue 语句,否则 while 循环和 for 循环是等效的。如果您有 continue 语句,它们是不等价的。我还注意到for (i = 0; ...)for (int i = 0; ...) 之间的细微差别,这不是问题所在。
    • 我确实认为您对未定义行为的调用是无意且可修复的 (i = 0; while (i &lt; n) { y[i] = x[i]; i++; })。如果您的意图是调查未定义行为的好处或其他方面,那么这与您的问题看起来有些不同。
    【解决方案3】:

    好问题, 看看here 你可以找到一个不错的 c to assembler web 编译器 我比较了两个版本,在循环实现中存在细微的组装差异。 功能似乎完全相同,因此我得出结论,两个样本总是相同的。 而案例: 对于案例:

    【讨论】:

    • 您找到了一个编译器,它使用特定的设置,恰好为两个示例生成等效的代码。然而,因为第一个例子调用了未定义的行为,这只是一个可能的结果。
    • 是的,它总是依赖于编译器。但它的站点使用 GNU gcc,它被认为是最好的编译器之一。
    猜你喜欢
    • 2016-03-25
    • 1970-01-01
    • 2018-10-04
    • 2019-03-25
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    相关资源
    最近更新 更多