简体   繁体   English

如何在c中的循环内访问递增的值

[英]How to access incremented values inside a loop in c

I'm new in programming and I'm stuck on code that reads the integer N from input and prints the first N integers of the following series:我是编程新手,我一直坚持从输入中读取整数 N 并打印以下系列的前 N ​​个整数的代码:

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

for example if the N is 4 it prints:例如,如果 N 是 4,它会打印:

1, -1, -2, 2

I wrote my code and it contains a while loop.我写了我的代码,它包含一个while循环。 I need the last value of t and i , which are incremented in the loop, outside of the loop but it doesn't work.我需要ti的最后一个值,它们在循环中在循环外递增,但它不起作用。

This is my code:这是我的代码:

#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;
}

Actually I need that outer part of the code for the odd N's (because when N is an even number like 4, numbers are paired like 1, -1, -2, 2 but when it's odd the last number is single like N=5,1, -1, -2, 2, 3 ).实际上,我需要奇数 N 的代码的外部部分(因为当 N 是像 4 这样的偶数时,数字像 1、-1、-2、2 一样成对,但是当它是奇数时,最后一个数字是单个像 N=5 ,1, -1, -2, 2, 3 )。 For even N's it works but for the odd's it prints 0 instead of the last number of the series.对于偶数 N,它有效,但对于奇数,它打印 0 而不是系列的最后一个数字。

Sorry if my explanation is not clear but I would appreciate any help.抱歉,如果我的解释不清楚,但我将不胜感激。

You can see replay about the definition of internal variable k hidden outer one.你可以看到关于内部变量 k 隐藏外部变量的定义的回放。 I'd like to say about the code.我想说一下代码。

In my view, there are two cases in the code: the first, you increment number;在我看来,代码中有两种情况:第一种,你递增数字; the second, you invert the sign.第二,你反转符号。 Also, you print a number in both cases.此外,您在两种情况下都打印一个数字。 So, I think, it can be implemented a little clearer:所以,我认为,它可以更清晰地实现:

#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;
}

On Some Iterative Improvements to the Code关于代码的一些迭代改进

There are at least a couple of fundamental problems in the OP posted code. OP 发布的代码中至少存在几个基本问​​题。

int t = i inside the loop shadows the t defined outside of the loop, and this inner t is lost when the loop is exited. int t = i的循环的阴影内的t限定的回路之外,并且该内t时退出循环丢失。 At this point, the value of t in the expression -t / i * (n + 1) / 2) is 1, the value that t was initialized to in this scope.在这点上,值t表达式-t / i * (n + 1) / 2)为1,该值t在此范围初始化为。 This problem can be fixed by changing int t = i --> t = i in the loop, utilizing the variable t that exists outside of the loop, and allowing that variable to be changed in the loop, but still used after the loop exits.这个问题可以通过在循环中改变int t = i --> t = i来解决,利用存在于循环外的变量t ,并允许该变量在循环中改变,但在循环退出后仍然使用.

But here we come to another problem.但在这里我们遇到了另一个问题。 t is set to the value of i at the top of the loop, but before the loop exits i is incremented. t在循环顶部设置为i的值,但在循环退出之前i递增。 This means that the value of i is greater than the value of t when the loop exits.这意味着当循环退出时i的值大于t的值。 Thus -t / i is zero in the final print statement!因此-t / i在最终的打印语句中为零! One way to fix this would be to change the final print statement from printf("%d", -t / i * (n + 1) / 2) to printf("%d", -t / (i-1) * (n + 1) / 2) .解决此问题的一种方法是将最终打印语句从printf("%d", -t / i * (n + 1) / 2)更改为printf("%d", -t / (i-1) * (n + 1) / 2) Note that this solution has a serious problem when i == 1 ;请注意,当i == 1时,此解决方案存在严重问题; this happens when n == 1 .n == 1时会发生这种情况。

Since we are really only after the sign of -t here, it might be more clear to use a conditional operator :因为我们真的只是在-t的符号之后,所以使用条件运算符可能更清楚:

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

Now that i is no longer needed outside of the loop, we should consider reducing the scope of i .现在循环外不再需要i ,我们应该考虑减少i的范围。 A good general principle is to keep the scopes of variables as small as practically possible.一个好的一般原则是将变量的范围尽可能小。 To do this, a for loop seems appropriate:为此, 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);
}

Note now that there are still some significant problems with the code.现在请注意,代码仍然存在一些重大问题。 An input of 1 skips the loop entirely and leads to the incorrect output -1 .输入 1 完全跳过循环并导致不正确的输出-1 A newline is never printed after the end of the series (this is just bad form), but worse, even inputs lead to a series printed with a comma and a space on the end!在系列结束后永远不会打印换行符(这只是错误的形式),但更糟糕的是,即使是输入也会导致系列在末尾打印一个逗号和一个空格! Formatting is important, and looking at the edge cases helps us to avoid bugs.格式化很重要,查看边缘情况有助于我们避免错误。 In this case, the first number in a series of only one term is wrong.在这种情况下,只有一个项的系列中的第一个数字是错误的。 In a couple of previous (now deleted) answers the solutions partially worked, but gave incorrect results in the last number when the input was an odd number (for every second odd number).在之前的几个(现已删除)答案中,解决方案部分有效,但是当输入为奇数时(每隔一个奇数),最后一个数字给出了错误的结果。 Lesson: investigate the edge cases when you code.课程:调查编码时的边缘情况。

When the input is 1, the loop is never entered, but we only need to print the first number of the series.当输入为 1 时,永远不会进入循环,但我们只需要打印系列的第一个数字。 A simple way to handle this is just to handle that input explicitly before the loop.处理这个问题的一种简单方法就是在循环之前显式处理该输入。

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

To handle the formatting issue, consider that each pass through the loop prints a pair of numbers.要处理格式问题,请考虑每次通过循环都会打印一对数字。 After the last pass, OP code prints one more line when input is an odd number.在最后一次通过后,当输入为奇数时,OP 代码会再打印一行。 Instead, handle the last pass through the loop explicitly.相反,明确处理最后一次通过循环。 If the loop is in its final iteration, print three numbers when input is odd, or print two numbers when input is even.如果循环处于最后一次迭代,则在输入为奇数时打印三个数字,或在输入为偶数时打印两个数字。 Otherwise, in the typical case, print a pair of numbers with commas as before.否则,在典型情况下,像以前一样用逗号打印一对数字。 Coding in this way allows all series code, except for the special case when input is 1 , to be brought inside of the loop, including allowing t to be defined inside the loop (further reducing the scope of t ).以这种方式编码允许将所有系列代码(输入为1的特殊情况除外)带入循环内部,包括允许在循环内部定义t (进一步缩小t的范围)。 Here is what that code looks like:这是该代码的样子:

#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;
}

On Input Validation关于输入验证

OP code does not check for valid input from the user. OP 代码不检查用户的有效输入。 In practice, code as if users are malicious and will provide any input to break your code.在实践中,编码就好像用户是恶意的一样,并且会提供任何输入来破坏您的代码。 Even without malicious intent, a user may accidentally provide improper input.即使没有恶意,用户也可能会意外地提供不正确的输入。 In the OP posted code, if a user inadvertently enters a letter, or anything other than a number, no value is stored in n .在 OP 发布的代码中,如果用户无意中输入了字母或数字以外的任何内容,则n不会存储任何值。 In such a case n has indeterminate value , and further attempts to use n will result in undefined behavior .在这种情况下n具有不确定的值,进一步尝试使用n将导致未定义的行为 Note that scanf() returns the number of successful assignments made, and checking this value is a part of any input validation (you should almost always check the result returned by any function that returns a value).请注意, scanf()返回成功分配的次数,检查此值是任何输入验证的一部分(您几乎应该始终检查任何返回值的函数返回的结果)。

You could check this value, and abort the program if the user fails to enter a number.您可以检查此值,如果用户未能输入数字,则中止程序。 This is not graceful.这并不优雅。 A better solution might be to take input in a loop, checking to see if a successful assignment was made by scanf() , and looping until a number is entered.更好的解决方案可能是在循环中输入,检查scanf()是否成功分配,然后循环直到输入数字。 This is more complicated than it sounds because the bad (and unassigned) input remains in the input stream after the call to scanf() , and needs to be cleared before progress can be made.这比听起来更复杂,因为在调用scanf()之后,错误的(和未分配的)输入仍保留在输入流中,并且需要在进行之前清除。 The scanf() function is difficult to use correctly, and a better solution is to use fgets() which reads a line of input into a buffer. scanf()函数很难正确使用,更好的解决方案是使用fgets()一行输入读取到缓冲区中。 After this, the buffer can be parsed with sscanf() .在此之后,可以使用sscanf()解析缓冲区。

The fgets() function returns a null pointer in the unlikely event of a read error; fgets()函数在不太可能发生读取错误的情况下返回一个空指针; robust code checks for such errors.健壮的代码检查此类错误。 The code below places the series printing code in a function print_series() , and takes and does very basic validation of input in main() .下面的代码将系列打印代码放在函数print_series() ,并在main()对输入进行非常基本的验证。 If there is an input read error, the code simply prints an error message and exits.如果存在输入读取错误,则代码仅打印错误消息并退出。 Note that the input[] array is used as an input buffer, and it is generously sized so that normal input should be contained within it.请注意, input[]数组用作输入缓冲区,并且它的大小很大,因此应该包含正常输入。 More robust code would plan for the possibility that more input could arrive than can be held in this buffer.更健壮的代码将计划可能出现的输入可能超过此缓冲区中所能容纳的数量。 fgets() will not overflow this buffer, but extra input would remain in the input stream in such a case, potentially causing problems for later I/O operations. fgets()不会溢出此缓冲区,但在这种情况下,额外的输入会保留在输入流中,这可能会导致以后的 I/O 操作出现问题。

If no number can be parsed by sscanf() , the input loop continues, printing another prompt and fetching a fresh line of input into the input buffer.如果sscanf()无法解析任何数字,则输入循环将继续,打印另一个提示并将新的输入行提取到输入缓冲区中。 Once a number has been assigned to max , another loop prints all series from length 1 to length max ;一旦将一个数字分配给max ,另一个循环将打印从长度 1 到长度max所有系列; this helps us to see that the series outputs are as expected for different values of N. Note that when the input is less than 1 for max , the printing loop is not entered at all, and nothing is printed.这有助于我们看到对于不同的 N 值,系列输出符合预期。请注意,当max的输入小于1 ,根本不输入打印循环,也不会打印任何内容。 This may not be the best way to handle such inputs.这可能不是处理此类输入的最佳方式。

#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);
        }
    }
}

Sample runs:示例运行:

$ ./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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM