C For循环从循环scanf跳过第一次迭代和伪造数 [英] C For loop skips first iteration and bogus number from loop scanf

查看:79
本文介绍了C For循环从循环scanf跳过第一次迭代和伪造数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为学校创建邮件标签生成器,但遇到了一些问题.我的程序将采用0到10之间的个人的全名,地址,城市,州和邮政编码.运行我的程序时,我遇到两个主要问题.for循环跳过全名"safergets()",然后转到地址safergets.我继续查看其他所有功能是否正常,但是我对邮政编码的验证无法正常工作.我添加了一个printf,以查看输入的数字是否相同,并且发现它是虚假的.另外,我在尝试大写状态输出的行中收到错误代码.我确定我没有正确使用toupper.以下是我的代码,错误代码和输出.

I am creating a mailing label generator for school and am having an issue with a few problems. My program is to take the full name, address, city, state, and zip code for individuals from 0 to 10. When running my program I am having two major problems. The for loop skips the full name "safergets()" and moves on to the address safergets. I moved on to see if everything else works, but my verification for the zip code would not work correctly. I added a printf to see if the input was the same number and found it to be bogus. Also, I am getting an error code for my line attempting to capitalize the state output. I'm sure I am using toupper incorrectly. Attached below is my code, error code, and output.

#include <stdio.h>
#include <ctype.h>

/* Define structure */

struct information
{
    char full_name[35], address[50], city[25], state[3];
    long int zip_code;
};

/* Function safer_gets */
/* ------------------- */

void safer_gets (char array[], int max_chars)
{
  /* Declare variables. */
  /* ------------------ */

  int i;

  /* Read info from input buffer, character by character,   */
  /* up until the maximum number of possible characters.    */
  /* ------------------------------------------------------ */

  for (i = 0; i < max_chars; i++)
  {
     array[i] = getchar();


     /* If "this" character is the carriage return, exit loop */
     /* ----------------------------------------------------- */

     if (array[i] == '\n')
        break;

   } /* end for */

   /* If we have pulled out the most we can based on the size of array, */
   /* and, if there are more chars in the input buffer,                 */
   /* clear out the remaining chars in the buffer.                      */
   /* ----------------------------------------------------------------  */

   if (i == max_chars )

     if (array[i] != '\n')
       while (getchar() != '\n');

   /* At this point, i is pointing to the element after the last character */
   /* in the string. Terminate the string with the null terminator.        */
   /* -------------------------------------------------------------------- */

   array[i] = '\0';


} /* end safer_gets */

/* Begin main */

int main()
{
    /* Declare variables */

    struct information person[10];
    int x, i;

    /* Issue greeting */

    printf("Welcome to the mailing label generator program.\n\n");

    /* Prompt user for number of individuals between 0 - 10. If invalid, re-prompt */

    do
    {
        printf("How many people do you want to generate labels for (0-10)? ");
        scanf("%i", &x);

        if(x<0 || x>10)
        printf("Invalid number. Please re-enter number. Must be from 0 to 10.\n");

    }while(x<0 || x>10);

    /* Begin loop for individual information */

    for(i = 0; i < x; i++)
    {
        printf("\n\nEnter name: ");
        safer_gets(person[i].full_name, 35); /* This is the step being skipped */

        printf("\nEnter street address: ");
        safer_gets(person[i].address, 50);

        printf("\nEnter city: ");
        safer_gets(person[i].city, 25);

        printf("\nEnter state: ");
        gets(person[i].state);

        /* Begin loop to verify correct zipcode */

        do
        {
            printf("\nEnter zipcode: ");
            scanf("%ld", person[i].zip_code); /* I get a bogus number here */

            if(person[i].zip_code<00001 || person[i].zip_code>99999)
            {
                printf("\nInvalid zipcode. Must be from 00001 to 99999.");
            }
        }while(person[i].zip_code<00001 || person[i].zip_code>99999);
        /* end loop */

    }/* end of loop */

    /* Output individual information in mailing format, condition for 0 individuals */
    if(x>0 && x<10)
    {
    printf("\n\nBelow are your mailing labels:\n\n");
    }

    /* Begin loop for outputting individual(s) mailing labels */

    for(i = 0; i < x; i++)
    {
        printf("%s\n",person[i].full_name);
        printf("%s\n",person[i].address);
        printf("%s\n",person[i].city);

        /* Output state in all uppercase */

        printf("%s\n", toupper(person[i].state)); /* This is where the error code is occurring */

        printf("%.5ld\n\n", person[i].zip_code);
    } /* end of loop */

    printf("Thank you for using the program.\n");

}/*end of main */

错误代码:142:警告:传递'toupper'的arg 1会使指针的整数不进行强制转换.

Error code:142: warning: passing arg 1 of `toupper' makes integer from pointer without a cast.

输出:

Welcome to the mailing label generator program.

How many people do you want to generate labels for (0-10)? 1


Enter name:
Enter street address: 100 Needhelp Ave.

Enter city: Gardner

Enter state: NY

Enter zipcode: 01420

Invalid zipcode. Must be from 00001 to 99999.
Enter zipcode:

我在这里查看了几个问题,试图了解我要去哪里,但是如果感觉到可能会影响我的程序的几个问题.此外,我们的教授还为我的班级提供了safergets函数,以确保用户输入的字符数不超过数组可以容纳的字符数.感谢您的帮助和宽容,帮助我理解我的错误!

I have looked at several questions on here to try and understand where I am going wrong, but if feel it might be a couple of issues effecting my program. Also, the safergets function was given to my class by our professor to ensure that the user does not input more characters than the array can hold. Thank you for your help and tolerance in helping me understand my mistakes!

推荐答案

让我们一一看一下这个问题:

Let's take a look at the problem one by one:

换行号或阅读人数后保留在标准输入中

    printf("\n\nEnter name: ");
    safer_gets(person[i].full_name, 35); /* This is the step being skipped */

由于您的 safer_gets()仅读取第一个'\ n'(换行符字符-而不是回车,即'\ r').但是,在输入流中看到的第一个字符 saver_gets()'\ n'字符,该字符在您调用后仍未读入 stdin scanf 在:

It is skipped because your safer_gets() reads only to the first '\n' (newline character -- not a carriage-return, that is '\r'). However, the first character saver_gets() sees in the input stream is the '\n' character that remains in stdin unread after your call to scanf in:

    printf("How many people do you want to generate labels for (0-10)? ");
    scanf("%i", &x);

用于数字转换的所有 scanf 格式说明符只能读取组成数字的最后一个数字(或小数点),最后一个数字则保留'\ n'<用户在输入流中按 Enter 未读生成的/code>(此处为 stdin ).这是鼓励新的C程序员阅读使用面向 line _ 的输入功能(例如 fgets()(或POSIX getline()),然后使用 sscanf 解析填充缓冲区中的值.

All scanf format specifiers for numeric conversion read only through the last digit (or decimal point) that makes up a number leaving the '\n' generated by the user pressing Enter unread in the input-stream (stdin here). This is one of the primary reasons new C programmers are encouraged to read user input with a line-oriented input function such as fgets() (or POSIX getline()) and then use sscanf to parse values from the filled buffer.

为什么用户首选使用行方向输入功能

通过使用具有足够缓冲区的面向行的输入函数,整个用户输入行将被消耗(包括用户按下输入).这样可以确保 stdin 准备好用于下一次输入,并且不会有先前输入中残留的未读字符在等着您.

By using a line-oriented input function with sufficient buffer, the complete line of user-input is consumed (including the '\n' from the user pressing Enter). This ensures that stdin is ready for the next input and doesn't have unread characters left-over from a prior input waiting to bite you.

正确使用所有输入功能

如果您没有从此答案中获得任何其他东西,请学习此知识-您不能正确使用任何输入函数,除非您 检查退货 .对于 scanf 系列功能尤其如此.为什么?如果您尝试使用 scanf 读取整数,而用户输入的是"four" ,则会出现匹配失败并从您的字符中提取字符输入流停止,第一个无效字符将输入流中所有有问题的字符保留为未读.(等着再次咬你).

If you take nothing else from this answer, learn this -- you cannot use any input function correctly unless you check the return. This is especially true for the scanf family of functions. Why? If you are attempting to read an integer with scanf and the user enters "four" instead, then a matching failure occurs and character extraction from your input stream ceases with the first invalid character leaving all offending characters in your input stream unread. (just waiting to bite you again).

正确使用scanf

scanf .这意味着负责每次检查 scanf 返回.您必须处理三个条件

scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions

  1. (return == EOF)用户通过按 Ctrl + d (或在Windows EOF 来取消输入> Ctrl + z );
  2. (返回<预期的转换数量)发生匹配 input 失败.对于 matching 失败,您必须考虑输入缓冲区中剩余的每个字符.(在输入缓冲区中向前扫描以读取和丢弃字符,直到找到'\ n' EOF );最后
  3. (返回==预期的转换数量)表示读取成功-然后由您检查输入是否满足任何其他条件(例如,正整数,正浮点数),在所需范围之内,等等.)
  1. (return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z);
  2. (return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
  3. (return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).

在使用 scanf 成功读取之后,您还必须考虑输入流中剩余的内容.如上所述, scanf 将使所有转换说明符的输入流中的'\ n'保持未读状态,除非您专门在 format字符串中进行说明(如果考虑在内,通常会导致输入格式字符串易碎,在所需输入之后但在'\ n'之前,很容易被其他多余的字符挫败).使用 scanf 作为输入,您必须戴上会计帽,并考虑输入流中剩余的每个字符,并在需要时清空输入流中任何有问题的字符.

You also must account for what is left in your input stream after a successful read with scanf. As discussed above, scanf will leave the '\n' in the input stream unread for ALL conversion specifiers unless you specifically account for it in your format string (which, if accounted for, usually results in a fragile input format string, easily foiled by additional extraneous characters after the wanted input but before the '\n') When using scanf for input, you must put your accountant's hat on and account for every character that remains in the input stream and empty the input stream of any offending characters when required.

您可以编写一个简单的 empty_stdin()函数,通过简单地向前扫描丢弃直到'\ n'已找到或遇到 EOF .您可以在 safer_gets()函数中进行不同程度的操作.您可以编写一个简单的函数,如下所示:

You can write a simple empty_stdin() function to handle removing all extraneous characters that remain after a user input by simply scanning forward discarding all characters that remain until the '\n' is found or EOF encountered. You do that to a varying degree in your safer_gets() function. You can write a simple function as:

void empty_stdin(void)
{
    int c = getchar();                /* read character */

    while (c != '\n' && c != EOF)     /* if not '\n' and not EOF */
        c = getchar();                /* repeat */
}

您可以使用简单的 for 循环内联执行相同的操作,例如

You can do the same thing with a simple for loop inline, e.g.

for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}

下一个问题-尝试写入无效地址

使用 scanf 时, scanf 期望相应转换的参数是指向适当类型的 pointer .在:

When using scanf, scanf expects the parameter for a corresponding conversion to be a pointer to the appropriate type. In:

         printf("\nEnter zipcode: ");
         scanf("%ld", person[i].zip_code); /* I get a bogus number here */

您无法提供指针,而是提供了 long int 值.由于 person [i] .zip_code long int 类型,以便为 scanf 提供 pointer 来填充您必须使用 address-of 运算符,例如& person [i] .zip_code 告诉 scanf 用哪个地址填充其提供转换的值.

You fail to provide a pointer, providing a long int value instead. Since person[i].zip_code is type long int in order to provide a pointer for scanf to fill you must use the address-of operator, e.g. &person[i].zip_code to tell scanf which address to fill with the value it provides a conversion for.

等等吗?为什么我不必对数组进行操作?在访问时,数组会转换为指向第一个元素的指针.因此,对于字符串输入,如果使用数组保存字符串,则会自动将其转换为指针

Wait? Why don't I have to do that with array? On access, an array is converted to a pointer to the first element. So for string input, if an array is being used to hold the string, it is automatically converted to a pointer C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3).

上端操作字符而不是字符串

    printf("%s\n", toupper(person[i].state)); /* This is where the error code is occurring */

如我的评论中所述, toupper 将类型为 int 作为参数,而不是类型为 char * .要将字符串转换为大写/小写,您需要遍历每个字符,分别转换每个字符.但是,如果您使用结构的 .state 成员,则只需担心2个字符,因此只需在读取它们时将它们都转换即可,例如

As discussed in my comment, toupper takes type int as the parameter, not type char*. To convert a string to upper/lower case, you need to loop over each character converting each character individually. However, in your case with the .state member of your struct, there are only 2 characters to worry about, so just convert them both when they are read, e.g.

            /* just 2-chars to convert to upper - do it here */
            person[i].state[0] = toupper (person[i].state[0]);
            person[i].state[1] = toupper (person[i].state[1]);

safer_gets()中的基本问题

这可以解决大多数明显的问题,但是 safer_gets()函数本身具有几个基本问​​题.具体来说,当 getchar()返回时,它无法处理 EOF ,并且由于无法返回任何内容,因此无法向用户提供任何指示是请求的用户输入是成功还是失败.类型为 void .在编写的任何函数中,如果函数内可能发生故障,则必须向调用函数提供有意义的返回,以指示所请求的函数操作成功还是失败.

That takes care of the most of the obvious problems, but the safer_gets() function itself has several fundamental problems. Specifically, it fails to handle EOF when returned by getchar() and it fails to provide any indication to the user whether the requested user-input succeeded or failed due to returning nothing with type void. In any function you write, where there is any possibility of failure within the function, you MUST provide a meaningful return to the calling function to indicate whether the requested operation of the function succeeded or failed.

您可以使用 safer_gets()做什么?为什么不返回提供成功读取字符数的简单 int 值,如果失败则返回 -1 ( EOF 的常规值).现在,您可以验证输入是否成功获得了双重奖励-您还获得了字符串中的字符数(限制为 2147483647 字符).现在,您还可以通过在Linux上使用 Ctrl + d 或在Windows上使用 Ctrl + z 生成手动EOF来处理用户取消输入的功能.

What can you do with safer_gets()? Why not return a simple int value providing the number of characters read on success, or -1 (the normal value for EOF) on failure. You get the double-bonus of now being able to validate whether the input succeeded -- and you also get the number of character in the string (limited to 2147483647 chars). You also now have the ability to handle a user canceling the input by generating a manual EOF with Ctrl+d on Linux or Ctrl+z (windows).

在除 EOF 以外的所有情况下,还应该将所有输入的所有字符的 stdin 清空.这样可以确保在调用 safer_gets()之后没有任何未读的字符会被咬住,如果以后再调用另一个输入函数的话.进行这些更改后,您可以将 safer_gets()编写为:

You should also empty stdin of all characters input in ALL cases other than EOF. This ensures there are no characters that remain unread after your call to safer_gets() that can bite you if you make a call to another input function later. Making those changes, you could write your safer_gets() as:

/* always provide a meaninful return to indicate success/failure */
int safer_gets (char *array, int max_chars)
{
    int c = 0, nchar = 0;

    /* loop while room in array and char read isn't '\n' or EOF */
    while (nchar + 1 < max_chars && (c = getchar()) != '\n' && c != EOF)
        array[nchar++] = c;         /* assing to array, increment index */
    array[nchar] = 0;               /* nul-terminate array on loop exit */

    while (c != EOF && c != '\n')   /* read/discard until newline or EOF */
        c = getchar();

    /* if c == EOF and no chars read, return -1, otherwise no. of chars */
    return c == EOF && !nchar ? -1 : nchar;
}

(注意: nchar + 1< max_chars 上的测试上方,可确保为 nul-termination 字符保留一个字符,并且只是 nchar< max_chars-1 )

(note: above the test on nchar + 1 < max_chars ensures that a character remains for the nul-terminating character, and is just a safer rearrangement of nchar < max_chars - 1)

输入验证的一般方法

现在,您可以使用一个输入函数,该输入函数指示输入成功/失败,从而使您可以在调用函数中重新验证输入(此处为 main()).以使用 safer_gets()读取 .full_name 成员为例.您不能只是盲目地调用 safer_gets()并且不知道输入是被取消还是遇到过早的 EOF 并使用,然后继续使用它充满信心的字符串您的代码. *验证,验证,确认每个表达式.回到 main()中,您可以通过如下调用 safer_gets()来读取 .full_name (以及其他所有字符串变量)来实现:

Now, you have an input function you can use that indicates success/failure of input allowing you to validate the input back in the calling function (main() here). Take for example reading the .full_name member using safer_gets(). You can't just blindly call safer_gets() and not know whether input was canceled or a premature EOF encountered and use then proceed to use the string it filled with any confidence in your code. *Validate, validate, validate each expression. Back in main(), you can do that by calling safer_gets() as follows to read .full_name (and every other string variable):

#define NAMELEN 35  /* if you need a constant, #define one (or more) */
#define ADDRLEN 50  /*         (don't skimp on buffer size)          */
...
        for (;;) {      /* loop continually until valid name input */
            fputs ("\nEnter name           : ", stdout);            /* prompt */
            int rtn = safer_gets(person[i].full_name, NAMELEN);     /* read name */
            if (rtn == -1) {        /* user canceled input */
                puts ("(user canceled input)");
                return 1;           /* handle with graceful exit */
            }
            else if (rtn == 0) {    /* if name empty - handle error */
                fputs ("  error: full_name empty.\n", stderr);
                continue;           /* try again */
            }
            else                    /* good input */
                break;
        }

(注意: safer_gets()的返回值被捕获在变量 rtn 中,然后被评估为 -1 ( EOF ), 0 空字符串或大于 0 的良好输入)

(note: the return of safer_gets() is captured in the variable rtn and then evaluated for -1 (EOF), 0 empty-string, or greater than 0, good input)

您可以为每个需要使用的字符串变量执行此操作,然后使用上面讨论的相同原理读取和验证 .zip_code .简而言之,您可以执行以下操作:

You can do that for each string variable you need to use, and then use the same principals discussed above to read and validate .zip_code. Putting it altogether in a short example, you could do:

#include <stdio.h>
#include <ctype.h>

#define NAMELEN 35  /* if you need a constant, #define one (or more) */
#define ADDRLEN 50  /*         (don't skimp on buffer size)          */
#define CITYLEN 25
#define STATELEN 3
#define PERSONS 10

struct information {
    char full_name[NAMELEN],
        address[ADDRLEN],
        city[CITYLEN],
        state[STATELEN];
    long int zip_code;
};

/* always provide a meaninful return to indicate success/failure */
int safer_gets (char *array, int max_chars)
{
    int c = 0, nchar = 0;

    /* loop while room in array and char read isn't '\n' or EOF */
    while (nchar + 1 < max_chars && (c = getchar()) != '\n' && c != EOF)
        array[nchar++] = c;         /* assing to array, increment index */
    array[nchar] = 0;               /* nul-terminate array on loop exit */

    while (c != EOF && c != '\n')   /* read/discard until newline or EOF */
        c = getchar();

    /* if c == EOF and no chars read, return -1, otherwise no. of chars */
    return c == EOF && !nchar ? -1 : nchar;
}

int main (void) {

    /* declare varaibles, initialize to all zero */
    struct information person[PERSONS] = {{ .full_name = "" }};
    int i = 0, x = 0;

    puts ("\nWelcome to the mailing label generator program.\n");   /* greeting */

    for (;;) {          /* loop continually until a valid no. of people entered */
        int rtn = 0;    /* variable to hold RETURN from scanf */

        fputs ("Number of people to generate labels for? (0-10): ", stdout);
        rtn = scanf ("%d", &x);

        if (rtn == EOF) {   /* user generated manual EOF (ctrl+d [ctrl+z windows]) */
            puts ("(user canceled input)");
            return 0;
        }
        else {  /* either good input or (matching failure or out-of-range) */
            /* all required clearing though newline - do that here */
            for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}

            if (rtn == 1) { /* return equals requested conversions - good input */
                if (0 <= x && x <= PERSONS) /* validate input in range */
                    break;                  /* all checks passed, break read loop */
                else                        /* otherwise, input out of range */
                    fprintf (stderr, "  error: %d, not in range 0 - %d.\n",
                            x, PERSONS);
            }
            else    /* matching failure */
                fputs ("  error: invalid integer input.\n", stderr);
        }
    }
    if (!x) {   /* since zero is a valid input, check here, exit if zero requested */
        fputs ("\nzero persons requested - nothing further to do.\n", stdout);
        return 0;
    }

    /* Begin loop for individual information */

    for (i = 0; i < x; i++) {   /* loop until all person filled */

        /* read name, address, city, state */
        for (;;) {      /* loop continually until valid name input */
            fputs ("\nEnter name           : ", stdout);            /* prompt */
            int rtn = safer_gets(person[i].full_name, NAMELEN);     /* read name */
            if (rtn == -1) {        /* user canceled input */
                puts ("(user canceled input)");
                return 1;           /* handle with graceful exit */
            }
            else if (rtn == 0) {    /* if name empty - handle error */
                fputs ("  error: full_name empty.\n", stderr);
                continue;           /* try again */
            }
            else                    /* good input */
                break;
        }

        for (;;) {      /* loop continually until valid street input */
            fputs ("Enter street address : ", stdout);              /* prompt */
            int rtn = safer_gets(person[i].address, ADDRLEN);       /* read address */
            if (rtn == -1) {        /* user canceled input */
                puts ("(user canceled input)");
                return 1;           /* handle with graceful exit */
            }
            else if (rtn == 0) {    /* if address empty - handle error */
                fputs ("error: street address empty.\n", stderr);
                continue;           /* try again */
            }
            else                    /* good input */
                break;
        }

        for (;;) {      /* loop continually until valid city input */
            fputs ("Enter city           : ", stdout);              /* prompt */
            int rtn = safer_gets(person[i].city, CITYLEN);          /* read city */
            if (rtn == -1) {        /* user canceled input */
                puts ("(user canceled input)");
                return 1;           /* handle with graceful exit */
            }
            else if (rtn == 0) {    /* if city empty - handle error */
                fputs ("error: city empty.\n", stderr);
                continue;           /* try again */
            }
            else                    /* good input */
                break;
        }

        for (;;) {      /* loop continually until valid state input */
            fputs ("Enter state          : ", stdout);              /* prompt */
            int rtn = safer_gets(person[i].state, STATELEN);        /* read state */
            if (rtn == -1) {        /* user canceled input */
                puts ("(user canceled input)");
                return 1;           /* handle with graceful exit */
            }
            else if (rtn == 0) {    /* if state empty - handle error */
                fputs ("error: state empty.\n", stderr);
                continue;           /* try again */
            }
            else {                  /* good input */
                /* just 2-chars to convert to upper - do it here */
                person[i].state[0] = toupper (person[i].state[0]);
                person[i].state[1] = toupper (person[i].state[1]);
                break;
            }
        }

        /* read/validate zipcode */
        for (;;) {      /* loop continually until valid zipcode input */
            fputs ("Enter zipcode        : ", stdout);              /* prompt */
            int rtn = scanf ("%ld", &person[i].zip_code);           /* read zip */

            if (rtn == EOF) {   /* user pressed ctrl+d [ctrl+z windows] */
                puts ("(user canceled input)");
                return 1;
            }
            else {      /* handle all other cases */
                /* remove all chars through newline or EOF */
                for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}

                if (rtn == 1) {    /* long int read */
                    /* validate in range */
                    if (1 <= person[i].zip_code && person[i].zip_code <= 99999)
                        break;
                    else
                        fprintf (stderr, "  error: %ld not in range of 1 - 99999.\n",
                                person[i].zip_code);
                }
                else    /* matching failure */
                    fputs ("  error: invalid long integer input.\n", stderr);
            }
        }
    }

    /* Output individual information in mailing format, condition for 0 individuals */
    for(i = 0; i < x; i++)
        /* you only need a single printf */
        printf ("\n%s\n%s\n%s, %s %ld\n", person[i].full_name, person[i].address,
                person[i].city, person[i].state, person[i].zip_code);

    fputs ("\nThank you for using the program.\n", stdout);
}

(注意:,通过使用 #define 创建所需的常数,如果需要调整数字,则只有一个地方可以进行更改,并且尽管每个变量声明和循环限制都试图进行更改,但我们仍然没有选择)

(note: by using #define to create the needed constants, if you need to adjust a number, you have a single place to make the change and you are not left picking though each variable declaration and loop limit to try and make the change)

使用/输出示例

完成任何输入例程的编写后,请尝试破坏它!查找失败的极端情况并修复它们.继续尝试通过故意输入不正确/无效的输入来打破它,直到它不再除用户需要输入的内容以外.练习输入例程,例如

When you have finished writing any input routine -- go try and break it! Find the corner-cases that fail and fix them. Keep trying to break it by intentionally entering incorrect/invalid input until it no longer excepts anything but what the user is required to input. Exercise your input routines, e.g.

$ ./bin/nameaddrstruct

Welcome to the mailing label generator program.

Number of people to generate labels for? (0-10): 3

Enter name           : Mickey Mouse
Enter street address : 111 Disney Ln.
Enter city           : Orlando
Enter state          : fL
Enter zipcode        : 44441

Enter name           : Minnie Mouse
Enter street address : 112 Disney Ln.
Enter city           : Orlando
Enter state          : Fl
Enter zipcode        : 44441

Enter name           : Pluto (the dog)
Enter street address : 111-b.yard Disney Ln.
Enter city           : Orlando
Enter state          : fl
Enter zipcode        : 44441

Mickey Mouse
111 Disney Ln.
Orlando, FL 44441

Minnie Mouse
112 Disney Ln.
Orlando, FL 44441

Pluto (the dog)
111-b.yard Disney Ln.
Orlando, FL 44441

Thank you for using the program.

当用户希望在Linux上使用 Ctrl + d Ctrl + z (Windows)生成手动EOF时,希望用户随时取消输入,您应该能够从您的代码中的任何地方进行处理.

Respecting the users wish to cancel input at any point when they generates a manual EOF with Ctrl+d on Linux or Ctrl+z (windows), you should be able to handle that from any point in your code.

在第一个提示处:

$ ./bin/nameaddrstruct

Welcome to the mailing label generator program.

Number of people to generate labels for? (0-10): (user canceled input)

或此后的任何提示:

$ ./bin/nameaddrstruct

Welcome to the mailing label generator program.

Number of people to generate labels for? (0-10): 3

Enter name           : Mickey Mouse
Enter street address : 111 Disney Ln.
Enter city           : (user canceled input)

处理零人请求:

$ ./bin/nameaddrstruct

Welcome to the mailing label generator program.

Number of people to generate labels for? (0-10): 0

zero persons requested - nothing further to do.

(**个人,我只是更改输入测试,而是让他们输入 1-10 中的值)

(**personally, I would just change the input test and have them enter a value from 1-10 instead)

无效输入:

$ ./bin/nameaddrstruct

Welcome to the mailing label generator program.

Number of people to generate labels for? (0-10): -1
  error: -1, not in range 0 - 10.
Number of people to generate labels for? (0-10): 11
  error: 11, not in range 0 - 10.
Number of people to generate labels for? (0-10): banannas
  error: invalid integer input.
Number of people to generate labels for? (0-10): 10

Enter name           : (user canceled input)

您明白了...底线,您必须在使用程序中的输入之前验证每个用户输入并知道它是有效的.除非您 检查退货 ,否则无法验证任何功能的任何输入.如果您没有带走其他东西,那么学习是值得的.

You get the point... Bottom line, you must validate every user input and know it is valid before making use of the input in your program. You cannot validate any input from any function unless you check the return. If you take away nothing except that, the learning has been worthwhile.

仔细检查一下,如果还有其他问题,请告诉我.(并询问您的教授 safer_gets()如何处理 EOF ,以及如何验证功能是否成功)

Look things over and let me know if you have further questions. (and ask your prof. how safer_gets() handles EOF and how you are supposed to validate whether the funciton succeeded or failed)

这篇关于C For循环从循环scanf跳过第一次迭代和伪造数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆