如何在C中的循环内访问增量值

2020-02-15 c

我是编程的新手,但我坚持使用从输入中读取整数N并输出以下系列的前N个整数的代码:

1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, ...

例如,如果N为4,则打印:

1, -1, -2, 2

我编写了代码,其中包含一个while循环。我需要ti的最后一个值,它们在循环中,循环外递增,但不起作用。

这是我的代码:

#include <stdio.h>

int main(void) {
    int n;
    printf("Enter your number: ");
    scanf("%d", &n);

    int i = 1;
    int t = 1;
    while (i <= (n / 2)) {
        int t = i;
        if (i % 2 == 0)
            t *= -1;

        printf("%d", t);
        printf(", %d, ", -t);
        i++;
    }
    if (n % 2 == 1) {
        printf("%d", -t / i * (n + 1) / 2);
    }
    return 0;
}

实际上,我需要代码的外部为奇数N(因为当N是像4这样的偶数时,数字会像1,-1,-2、2这样配对,但是当它是奇数时,最后一个数字是单数,例如N = 5 ,1,-1,-2,2,3)。对于偶数N,它起作用,但对于奇数,它打印0而不是系列的最后一个数字。

抱歉,如果我的解释不清楚,请多多帮助。

Answers

您可以看到有关内部变量k隐藏外部1的定义的重放。我想谈谈代码。

我认为代码中有两种情况:第一,您增加数字;第二,您增加数字。第二,将符号反转。同样,在两种情况下,您都将打印一个数字。因此,我认为可以更清晰地实现它:

#include <stdio.h>

int main() {

    int n;
    printf("enter number:\n");
    scanf("%d",&n);

    for (int i = 0, sign = 1, j = 1; i < n; ++i) {
        if (i > 0)
            printf(", ");

        printf("%d",j*sign);

        if (i%2 == 0)
            sign = -sign;
        if (i%2 == 1)
            ++j;
    }

    putchar('\n');
    return 0;
}

关于代码的一些迭代改进

OP发布的代码中至少存在两个基本问题。

int t = i的循环的阴影内的t限定的回路之外,并且该内t时退出循环丢失。在这点上,值t表达式-t / i * (n + 1) / 2)为1,该值t在此范围初始化为。可以通过在循环中更改int t = i > t = i来解决此问题,利用存在于循环外部的变量t ,并允许在循环中更改该变量,但该变量在循环退出后仍会使用。

但是,这里我们遇到另一个问题。在循环的顶部将t设置为i的值,但在循环退出之前将i递增。这意味着当循环退出时, i的值大于t的值。因此, -t / i在最终的打印语句中为零!解决此问题的一种方法是将最终的打印语句从printf("%d", -t / i * (n + 1) / 2)更改为printf("%d", -t / (i-1) * (n + 1) / 2) 。请注意,当i == 1时,此解决方案存在严重问题。这在n == 1时发生。

由于实际上我们只在-t符号之后,所以使用条件运算符可能更清楚:

printf("%d", (-t < 0 ? -1 : 1) * (n + 1) / 2)

现在不再需要i了,我们应该考虑缩小i的范围。一个好的通用原则是使变量的范围尽可能地小。为此,一个for循环似乎是合适的:

int t = 1;
for (int i = 1; i <= (n / 2); i++) {  // reduce scope of `i`
    t = i;
    if (i % 2 == 0)
        t *= -1;

    printf("%d", t);
    printf(", %d, ", -t);
}
if (n % 2 == 1) {
    printf("%d", (-t < 0 ? -1 : 1) * (n + 1) / 2);
}

现在注意,代码仍然存在一些重大问题。输入1会完全跳过循环,并导致错误的输出-1 。在系列结束后再也不会打印换行符(这只是不好的形式),但是更糟糕的是,即使输入也导致在系列末尾加逗号和空格!格式化很重要,查看边缘情况有助于我们避免错误。在这种情况下,只有一个术语的序列中的第一个数字是错误的。在先前的几个答案(现在已删除)中,解决方案部分起作用,但是当输入为奇数(每隔一个奇数)时,最后一个数字给出的结果不正确。课程:研究编码时的极端情况。

当输入为1时,永远不会输入循环,但是我们只需要打印序列的第一个数字。一种简单的处理方法是在循环之前显式处理该输入。

if (n == 1) {           // n == 1: the loop is not entered
    printf("1\n");     // handle this special case
}

要处理格式问题,请考虑循环中的每次打印都会打印一对数字。在最后一次传递之后,当输入为奇数时,OP代码再打印一行。相反,应显式处理循环中的最后一次传递。如果循环处于最后迭代中,则在输入为奇数时打印三个数字,在输入为偶数时打印两个数字。否则,在典型情况下,请像以前一样打印一对带有逗号的数字。以这种方式进行编码允许将除输入为1的特殊情况以外的所有序列代码带入循环内部,包括允许在循环内部定义t (进一步减小t的范围)。该代码如下所示:

#include <stdio.h>

int main(void) {
    int n;
    printf("Enter your number: ");
    scanf("%d", &n);

    if (n == 1) {           // n == 1: the loop is not entered
        printf("1\n");     // handle this special case
    }

    for (int i = 1; i <= (n / 2); i++) {
        int t = i;         // reduced scope for `t`
        if (i % 2 == 0)
            t *= -1;

        if (i == n / 2) {  // print last numbers in the series
            if (n % 2) {   // n is odd: print 3 numbers
                printf("%d, %d, %d\n",
                       t,
                       -t,
                       (-t < 0 ? -1 : 1) * (n + 1) / 2);
            } else {       // n is even: print two numbers
                printf("%d, %d\n", t, -t);
            }
        } else {           // print typical pair in the series
            printf("%d, %d, ", t, -t);
        }
    }

    return 0;
}

输入验证时

OP代码不会检查用户的有效输入。在实践中,代码就像用户是恶意的一样,会提供任何输入来破坏您的代码。即使没有恶意,用户也可能会意外提供不正确的输入。在OP发布的代码中,如果用户无意输入字母或数字以外的任何内容,则n不会存储任何值。在这种情况下, n具有不确定的值 ,并且进一步尝试使用n将导致不确定的行为 。请注意, scanf()返回成功分配的次数,并且检查此值是任何输入验证的一部分(您几乎应该始终检查任何返回值的函数返回的结果)。

您可以检查该值,如果用户输入数字失败,则中止程序。这不是优雅的。更好的解决方案可能是在循环中接受输入,检查是否通过scanf()成功完成了赋值,然后循环进行直到输入数字为止。这比听起来更复杂,因为在调用scanf()之后,错误(且未分配)的输入仍保留在输入流中,并且需要清除后才能取得进展。 scanf()函数难以正确使用,更好的解决方案是使用fgets()来将一行输入读取到缓冲区中。此后,可以使用sscanf()解析缓冲区。

fgets()函数在不太可能发生读取错误的情况下返回空指针。健壮的代码检查此类错误。下面的代码将系列打印代码放置在函数print_series() ,并对main()的输入进行非常基本的验证。如果存在输入读取错误,则该代码仅打印一条错误消息并退出。请注意, input[]数组用作输入缓冲区,并且其大小适当,因此应在其中包含普通输入。更健壮的代码将为可能到达的输入量超出此缓冲区中的容量进行规划。 fgets()不会溢出此缓冲区,但是在这种情况下,多余的输入将保留在输入流中,从而可能在以后的I / O操作中引起问题。

如果sscanf()无法解析任何数字,则输入循环继续,打印另一个提示,并将新的输入行提取到输入缓冲区中。将数字分配给max ,另一个循环将打印从长度1到max所有序列;这有助于我们看到对于N的不同值而言,系列输出与预期的一样。请注意,当max的输入小于1 ,根本不会输入打印循环,并且不会打印任何内容。这可能不是处理此类输入的最佳方法。

#include <stdio.h>

#define MAX_INPUT  1000

void print_series(int n);

int main(void) {
    char input[MAX_INPUT];
    int max;

    // get user input and do minimal validation
    do {
        printf("Enter longest series to print: ");
        if (fgets(input, sizeof input, stdin) == NULL) {
            puts("Input Failure");
            return 1;
        }
    } while (sscanf(input, "%d", &max) != 1);

    // printing loop
    putchar('\n');
    for (int n = 1; n <= max; n++) {
        print_series(n);
    }

    return 0;
}

void print_series(int n) {
    if (n == 1) {
        printf("1\n");
    }

    for (int i = 1; i <= (n / 2); i++) {
        int t = i;
        if (i % 2 == 0)
            t *= -1;

        if (i == n / 2) {
            if (n % 2) {
                printf("%d, %d, %d\n",
                       t,
                       -t,
                       (-t < 0 ? -1 : 1) * (n + 1) / 2);
            } else {
                printf("%d, %d\n", t, -t);
            }
        } else {
            printf("%d, %d, ", t, -t);
        }
    }
}

样品运行:

$ ./a.out 
Enter longest series to print: bad input
Enter longest series to print: 0

$ ./a.out 
Enter longest series to print: 19

1
1, -1
1, -1, -2
1, -1, -2, 2
1, -1, -2, 2, 3
1, -1, -2, 2, 3, -3
1, -1, -2, 2, 3, -3, -4
1, -1, -2, 2, 3, -3, -4, 4
1, -1, -2, 2, 3, -3, -4, 4, 5
1, -1, -2, 2, 3, -3, -4, 4, 5, -5
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8, 8
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8, 8, 9
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8, 8, 9, -9
1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8, 8, 9, -9, -10

Related