【问题标题】:for(;...) or while(...) flow control? [closed]for(;...) 还是 while(...) 流控制? [关闭]
【发布时间】:2014-09-14 14:05:21
【问题描述】:

1 或 2 中哪一个更好(可以认为更好)?它们完全一样吗?

void method1(char **var1) {

  //the last element of var1 is NULL

  char **var2 = var1;
  int count = 0;

  //1
  for (; *var2; (*var2)++, count++);

  //2
  while(*var2) {
    (*var2)++;
    count++;
  }
}

【问题讨论】:

  • 下降编译器不会产生任何影响。等一下……可能会有一堆程序集作为答案。
  • 顺便说一句,我想你的意思是(var2)++ 而不是(*var2)++
  • @AlexanderSupertramp:那么代码应该做什么?递增指针var1[0],直到溢出为零?
  • Downvoting only 因为该问题缺少对代码示例应实现的目标的描述,同时 OP 对使其作为不经意的读者会期待。会很高兴解决这个问题。
  • @PavelŠimerda,我会克服的。

标签: c for-loop while-loop


【解决方案1】:

你可以用你的编译器检查不同优化级别的 asm 输出......或者只是不用担心语义相同的东西......

...

LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    movq    -16(%rbp), %rax
    cmpq    $0, (%rax)
    je  LBB0_4
## BB#2:                                ##   in Loop: Header=BB0_1 Depth=1
    jmp LBB0_3
LBB0_3:                                 ##   in Loop: Header=BB0_1 Depth=1
    movq    -16(%rbp), %rax
    movq    (%rax), %rcx
    addq    $1, %rcx
    movq    %rcx, (%rax)
    movl    -20(%rbp), %edx
    addl    $1, %edx
    movl    %edx, -20(%rbp)
    jmp LBB0_1
LBB0_4:
    ...


.subsections_via_symbols

方法2:

...

LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    movq    -16(%rbp), %rax
    cmpq    $0, (%rax)
    je  LBB0_3
## BB#2:                                ##   in Loop: Header=BB0_1 Depth=1
    movq    -16(%rbp), %rax
    movq    (%rax), %rcx
    addq    $1, %rcx
    movq    %rcx, (%rax)
    movl    -20(%rbp), %edx
    addl    $1, %edx
    movl    %edx, -20(%rbp)
    jmp LBB0_1
LBB0_3:
    ...


.subsections_via_symbols

【讨论】:

  • gcc -S 1.c -o 1.s 是给出的命令,你可以用优化标志检查
  • 你会看到它们基本上是一样的,一个结束了另一个标签......但这只是跳到下一行,反正会掉下来。
  • 为什么小跳转没有优化出来?
  • 未指定优化级别
  • 谢谢,只是想确认一下。所以我猜你的答案是三个有效答案之一,只是从不同的角度来看。
【解决方案2】:

相关代码的用途

您的代码似乎完全错误,因为它增加了var2 指针的目标,该指针也用于结束循环。您不能期望递增值达到零。我会假设(1)你想增加临时指针来迭代一个字符串列表(技术上是一个数组)和(2)你期望一个 NULL 指针作为哨兵。

指针自增问题详解

那么我们写的代码的逻辑是什么?它需要一个字符串数组(文件中的行、名称列表等),对项目进行计数,然后执行您需要做的任何其他事情。输入参数由指向 char 的指针表示,这对初学者来说可能有点混乱。指针在 C 中用于多种用途,一个是指向列表的第一项(技术上是数组)。这就是list 指针(char ** 类型)的情况,它指向一个指针数组(每个类型为char *),该数组又指向一个字节/字符值数组(每个类型char)。

因此,您需要增加一个本地 char ** 指针来迭代项目,并增加一个临时 char * 指针来迭代项目的字符。如果您只想读取数据,则绝不能增加本地(临时)变量以外的任何内容。递增*item 是无稽之谈,并且会以错误的方式更改数据(指针将指向第二个字符而不是第一个字符),并且检查递增的指针是否为 NULL 是双重废话。

换句话说,使用临时指针遍历数组的习惯用法需要以下操作:

  1. 在每一步都增加临时指针(仅此而已)。
  2. 检查指针的目标(而不是它指向的地址)的标记值。

更正的代码示例

使用 C99 语法,您可能想要执行以下操作:

void method1(char **list) {
    size_t count = 0;
    for (char **item = list; *item; item++)
        count++;
    ...
}

旧的语法迫使你这样做:

void method1(char **list) {
    char **item;
    size_t count = 0;

    for (item = list; *item; item++)
        count++;
    ...
}

对于不熟悉指针的人来说更直观的版本:

void method1(char **list) {
    size_t count = 0;
    for (size_t i = 0; list[i]; i++)
        count++;
    ...
}

注意:count 是多余的,因为它的值与 i 的值保持相同,因此您可以只使用空主体或 while (list[count]) count++; 来执行 for (; list[count]; count++)

只计算项目的真正功能是:

size_t get_size(char **list)
{
    int count = 0;
    for (char **item = list; *item; item++)
        count++;
    return count;
}

当然可以简化为(借用其他答案):

size_t get_size(char **list)
{
    int count = 0;
    for (; *list; list++)
        count++;
    return count;
}

感谢非常特殊的情况,其中 (1) 很容易合并 conditionincrement 以及 (2) 您没有使用正文中的当前项目,可转为:

size_t get_size(char **list)
{
    int count = 0;
    while (*list++)
        count++;
    return count;
}

尝试回答 forwhile 的困境

虽然从技术上讲 whilefor 循环是等价的,但 for 循环更好地表达了迭代习语方式,因为它将迭代逻辑与其余代码分开,并且因此也使其更可重用,即您可以将相同的 for 标头与不同的主体一起用于列表上的任何其他迭代操作。

原代码中for循环的错误使用

有很多事情应该被认为是不鼓励的:

1) 不要从 for 循环头修改对象。

for (... ; ...; (*item)++)
    ...

只要item 是指向实际数据的临时指针,任何符合上述模式的代码都会修改目标对象而不是执行循环逻辑。

2) 不要从 for 循环头中解耦任何非循环代码。

char **item = list;
...
for (; *item; *item++)
    count++;

for 循环之前的赋值似乎不合适。如果您复制粘贴 for 循环的标头以再次遍历所有列表项,则由于省略了初始化,该列表看起来是空的。

3) 不要在 for 循环头的 increment 中执行任何每个项目的操作。

for (char **item = list; *item++, count++)
    ;

这里的count++ 根本没有帮助循环,而是执行实际操作(计算一项)。如果您复制粘贴 for 循环的标头并添加实际正文,count 将被修改。

4) 不要对参数使用非描述性的,对临时变量使用简单的名称。

for (char **var2 = var1; *var2; var2++)
    count++;

这两个变量的用途不同,但它们的名称几乎相同,只是用一个数字来区分。您如何准确命名它们取决于上下文和偏好。

注意:有些人也更喜欢显式比较 NULL 而不是依赖于指针的布尔评估。不过,我不是其中之一。 Stack Exchange 似乎将 list 突出显示为关键字,但我认为 C 或 C++ 中没有这样的关键字。

【讨论】:

  • 投反对票的人是否愿意解释他的理由,或者这只是对投反对票的报复?在前一种情况下,我已准备好修复答案。
  • 不,不,他们可能不会……他们只是仇恨者。
  • 为什么在item是双指针的第一种情况下增加item++而不是(*item)++
  • @AlexanderSupertramp:需要一点想象力,甚至在纸上画才能理解在 C 中使用指针处理数组的逻辑。添加了相当彻底的解释,但您仍然需要努力理解内存指针的概念。
  • @AlexanderSupertramp:您是否知道输入参数表示字符串列表(正如注释中所建议的那样,NULL 是最后一个元素 / 无论如何都是不正确的,因为 NULL 不代表一个实际的元素)?
【解决方案3】:

如果您将 var2 初始化为 for 循环的第一个参数,我更喜欢 for 循环,即

for(char **var2 = var1; *var2; var2++)

因为所有条件(初始、终止、增量)都位于一个位置

我也希望明确测试,即,

for(char **var2 = var1; *var2 != NULL; var2++)

因为它使终端条件更加明显。

下一步:我不会将 count++ 放在 for 循环中,因为如果在循环内未修改 count ,则它是多余的,可以从 var2 - var 1 计算。如果在循环内修改了 count ,则应该完成在一个地方。

但我认为这只是个人喜好问题。

【讨论】:

  • *var2; 非常明确。添加!= NULL 简直是一团糟。
  • 我知道,但对他来说这还不够明确,因为他添加了评论...... - 你可以放弃那部分。
  • 为什么不把“count++”加到“for”上?
  • @AlexanderSupertramp:将count++ 放入循环体中。不要将与循环控制无关的代码塞入for 语句中。
  • @AlexanderSupertramp:我觉得有两个循环索引是多余的。如果两者不一致,也可能是错误的来源。我在回答中对 count++ 发表了评论。
【解决方案4】:

可能两者都相同,编译器应该没有任何区别。

【讨论】:

  • 确实如此,但不一定能回答问题。代码是为编译器和人类编写的。
【解决方案5】:

首先,两个循环都是错误的。他们没有任何意义。我想你的意思是以下

int count = 0;

while ( *var1++ ) ++count;

这是我会使用的循环。

或者,如果您希望 var1 不会被更改,那么

int count = 0;

for ( char **p = var1; *p; ++p ) ++count; 

你也可以写

char **p = var1;

while ( *p ) ++p;

int count = p - var1;

【讨论】:

  • 为什么要把 var1 改成 ++?我不想改变它。
  • var1char ** 所以(*var2)++ 可能正是他的意思。
  • @Carey Gregory 循环没有意义。
  • @Alexander Supertramp 当我已经展示了如何在不更改 var1 的情况下编写循环时,你为什么还要问同样的问题?
  • @Alexander Supertramp 有什么问题?你不能看我帖子里写的吗?试着再读一遍。
【解决方案6】:

您最好使循环条件语句更强大和更明确,以避免错误和无限循环。哪个更好取决于您的逻辑和代码,“for”循环更快更容易,但如果您想创建一个需要更多逻辑的循环,请使用“while”循环。

【讨论】:

  • 循环条件非常好。它不会导致错误或无限循环。我严重怀疑for 是否比while 快。
  • bu while 循环的输出方式与 for 循环的输出方式不同,因为在 for 循环的开头,如果语句为 true,则将 count++;首先然后执行 (*var2)++;但不久就会发生相反的事情。
  • 我从汇编语言中知道“for 循环”这一事实,我在这里可能错了:D 我只是想帮助你了解我所知道的。
猜你喜欢
  • 1970-01-01
  • 2021-01-10
  • 1970-01-01
  • 2010-11-23
  • 2023-04-09
  • 2014-02-08
  • 2013-08-07
  • 2011-12-28
  • 2010-09-18
相关资源
最近更新 更多