for(; ...)或while(...)流量控制?

时间:2014-09-14 14:05:21

标签: c for-loop while-loop

哪一个,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++;
  }
}

6 个答案:

答案 0 :(得分:2)

你可以用编译器检查不同优化级别的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

答案 1 :(得分:2)

有问题的代码的目的

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

指针递增问题的详细说明

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

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

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

  1. 在每一步增加临时指针(没有别的)。
  2. 检查指针的目标(而不是它指向的地址)以获取标记值。
  3. 更正了代码示例

    使用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的值保持一致,因此您可以for (; list[count]; count++)使用空体或while (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)很容易合并条件增量和(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循环标题的增量中执行任何每项操作。

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

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

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

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

    这两个变量的目的不同,但它们的名称几乎相同,只能用数字来区分。你如何命名它们是一个背景和偏好的问题。

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

答案 2 :(得分:1)

如果你将var2初始化为for循环的第一个参数,我宁愿使用for循环,即

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

因为所有条件(初始,终端,增量)都位于一个地方

我也希望明确地进行测试,即

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

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

下一篇:我不会在for循环中放置count++,因为如果在循环内部没有修改count,那么它是冗余的,可以从var2 - var 1中计算出来。如果在循环内修改了count,它应该可以在一个地方完成。

但我认为这只是一种品味问题。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

首先,两个循环都是错误的。他们毫无意义。我认为你的意思是以下

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;

答案 5 :(得分:-1)

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