【问题标题】:C For Loop Comparison Using Function Doesn't Wait使用函数的 C For 循环比较不等待
【发布时间】:2018-02-07 06:49:33
【问题描述】:

这有点奇怪,imo,我有一个用于比较的 for 循环 使用函数的返回值。

for (size_t i = 0; i < func(val); i++) {
    printf("Value from array modified in func = %u", values[i]);
}

问题是循环似乎在函数完成之前运行,如果在被调用函数中有一个 printf,则在循环输出之前会出现一些输出,这是预期的,有些是在预期之后,而不是预期的。该函数仅在 for 循环比较中调用,因此输出应该全部发生在 for 循环的输出之前。如果我使用 var 代替,例如:

size_t temp = func(val);

for (size_t i = 0; i < temp; i++) {
    printf("Value from array modified in func = %u", values[i]);
}

然后一切正常,这对我来说很奇怪,我没有使用线程所以这应该都是同步的吧?那么为什么循环不等待 func(val) 返回呢?

这是原始代码(剪掉一堆cmets):

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>

#define SUITS 4
#define FACES 13
#define CARDS 52
#define HAND 5

typedef struct {
    unsigned int suit;
    unsigned int face;
} card;

size_t checkX(card hand[HAND], unsigned int faces[HAND], unsigned int num);

int main() {

    card hand[HAND] = {{1,1},{1,7},{1,1},{1,4},{1,7}};

    const char *suit[SUITS] = {"Hearts", "Diamonds", "Clubs", "Spades"};
    const char *face[FACES] = {"Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"};

    unsigned int matchingFaces[HAND] = {0};

    for (size_t i = 0; i < checkX(hand, matchingFaces, 2); ++i) {
        printf("Pair of %s%s\n", face[matchingFaces[i]], matchingFaces[i] == 5 ? "es" : "s");
    }

}

size_t checkX(card hand[HAND], unsigned int faces[HAND], unsigned int num) {

    size_t facePos = 0; 

    for (size_t i = 0; i < HAND - 1; i++) {

        size_t count = 1;

        for (size_t j = i + 1; j < HAND; j++) {
            printf("%u == %u && %u != %u\n", hand[i].face, hand[j].face, hand[i].face, faces[facePos]);
            if (hand[i].face == hand[j].face && hand[i].face != faces[facePos]) count++;
        }

        faces[facePos] = hand[i].face;
        if (count == num) facePos++;
    }

    return facePos;
}

其中一些不是我的代码,我得到了一个基本的洗牌类型的程序,并告诉我修改它以处理一手牌并检查一对/三类。就像我说的那样,当我将 checkX 的返回值设置为变量然后将 i 与其进行比较而不是直接进行比较时,这确实有效,我只是不知道为什么。我已经尝试禁用 gcc 优化,但似乎没有什么不同。

直接函数比较的输出:

1 == 7 && 1 != 0
1 == 1 && 1 != 0
1 == 4 && 1 != 0
1 == 7 && 1 != 0
7 == 1 && 7 != 0
7 == 4 && 7 != 0
7 == 7 && 7 != 0
1 == 4 && 1 != 0
1 == 7 && 1 != 0
4 == 7 && 4 != 1
Pair of Deuces
1 == 7 && 1 != 1
1 == 1 && 1 != 1
1 == 4 && 1 != 1
1 == 7 && 1 != 1
7 == 1 && 7 != 1
7 == 4 && 7 != 1
7 == 7 && 7 != 1
1 == 4 && 1 != 7
1 == 7 && 1 != 7
4 == 7 && 4 != 1

使用变量间接比较func的输出:

1 == 7 && 1 != 0
1 == 1 && 1 != 0
1 == 4 && 1 != 0
1 == 7 && 1 != 0
7 == 1 && 7 != 0
7 == 4 && 7 != 0
7 == 7 && 7 != 0
1 == 4 && 1 != 0
1 == 7 && 1 != 0
4 == 7 && 4 != 1
Pair of Deuces
Pair of Eights

我知道这段代码不是最好的,但我必须使用给定的代码,只需要让它工作,所以这个问题只是出于好奇。

【问题讨论】:

  • 你意识到func()每次都通过循环被调用?如果它是一个常量值,那么只使用 var 会更有效...
  • 在无关紧要的注意事项上,始终记得在调用函数之前声明函数原型。如果没有声明函数,编译器将不得不猜测,而且并不总是能够正确猜测。
  • 我愿意,我只是把它放在这里,因为它并不重要。不过我现在会添加它。
  • 目前尚不清楚您认为此输出到底有什么问题。在每次循环迭代时调用该函数,并在每次迭代时打印输出。在某些迭代中,循环体也会打印一些东西。您是否希望所有检查都在循环体的所有执行之前、之后或其他什么时候发生?
  • 哦...这更有意义,我认为 checkX() 将被评估一次并且循环将运行。我不知道每次运行循环都会调用 checkX() 。感谢大家为我解决这个问题。

标签: c loops gcc


【解决方案1】:
for (size_t i = 0; i < func(val); i++) {
    printf("Value from array modified in func = %u", values[i]);
}

func(val)在这里被多次调用,每次需要决定是否继续循环,你可以把它想象成

size_t i = 0;
if (i < func(val)) {
    printf(.....);
    i++;
    if (i < func(val)) {
        printf(.....);
        i++;
        if (i < func(val)) {
            printf(.....);
            i++;
            if (i < func(val)) {
                printf(.....);
                i++;
                ....and so on......
            }
        }
    }
}

所以你可以看到,func(val) 可能会被多次调用。

【讨论】:

    【解决方案2】:

    Kernighan 和 Ritchie 所著的伟大著作《C 编程语言》中的一个相当不错但有点过时的部分,将 for 循环重新表述为等效的:

    {
        size_t i = 0;
        while (i < func(val)){
            printf("Value from array modified in func = %u", values[i]);
            i++;
        }
    }
    

    (外大括号是我的,是必需的,因为您现在可以在for 循环的第一个“表达式”内声明一个变量。)

    如果您以这种方式编写循环,很明显会看到 func(val) 在每次迭代中都被重新评估。这就是导致你观察到的效果的原因。 (请注意,并非所有语言都这样做,VBA 和 VB6 是不重新评估 for 循环的结束条件的两个示例。)

    因此,如果func(val) 是一个昂贵的函数并且您的算法不需要重复评估,您可能需要考虑提前预先计算该值。一些编译器可能会优化重新评估,特别是如果您使用像 strlen 这样的标准函数,但这样的优化是一门非常棘手的科学。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      相关资源
      最近更新 更多