-6

I'm trying to make a tiny account register program. It prompts the user for userID and password. I'm working on the userID part but it doesn't seem right. It works fine if I type in a < 8 characters userID, but when I type in a userID with more than 16 characters, the messages asking the user for a different userID are printed twice. Why's that?

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

int main(void)
{
    // Setting up variables
    char userID[18];
    char passcode[51];

    // Firstly promt the user for user ID
    do // This loop is for asking the user to type another userID if his chosen one isn't approprite
    {
        printf("Please choose your user ID! (it must be between 6 - 16 charaters long)\n");
        for (int i = 0; i < 17; i++) // This loop store the userID into the character array
        {
            userID[i] = getchar(); // Store userID into array character by         character
            if (userID[i] == '\n')
            {
                userID[i+1] = '\0';
                break;
            }

        }
    }
    while (strlen(userID) < 7  ||  (strlen(userID) > 16  &&  userID[16] != '\n'));

    // Printing info on the screen.
    printf("Your acount info are:\n\tuserID  : %s\tpasscode: ", userID);
halfer
  • 18,701
  • 13
  • 79
  • 158
  • 3
    Please post the code in order to know the error in your problem. Thank you. – VatsalSura Jul 09 '16 at 03:56
  • T.T I'm new so I can't post picture yet T.T. Can you have a look in the link :(. Really really apreciated – Sanh Nguyen Jul 09 '16 at 04:05
  • You don't have to post a picture. You can just post the code in the question itself. – VatsalSura Jul 09 '16 at 04:07
  • Welcome to Stack Overflow. Please read the [About] page soon, and about how to create an MCVE ([MCVE]) even more urgently. We don't want a picture; we want the code presented as text, copied and pasted. Pictures are useless — we can't copy'n'paste the code from a picture. And for pity's sake, don't show us code with warnings like 'unused variable' in them — get rid of the warnings. – Jonathan Leffler Jul 09 '16 at 04:08
  • 1
    You don't null terminate your input string unless the last character is newline, and if the string is too long, the last character isn't a newline. This leads to undefined behaviour. – Jonathan Leffler Jul 09 '16 at 04:14
  • First, I want to sorry and thank you all for your patience despite my totally messed up post. This is my first post. I'll improve my future post so they won't be like this one. Second, Thank you Jonathan Leffler for your answer, it's exacttly what I'm looking for. I'm looking for how to vote up your answer (to show my appreciation and for future experience on this site too) – Sanh Nguyen Jul 09 '16 at 04:28
  • 2
    My comment isn't an answer — it's just a diagnosis of the problem (well, one problem) and not the fix. You probably don't have enough rep to up-vote comments yet, but in due course you'll be able to do so. Don't worry about it; if I'd written an answer, then it would be different, but comments are nominally ephemeral (even though they can last years). – Jonathan Leffler Jul 09 '16 at 05:47
  • `userID[i] = getchar();` You need to read more about return type of ´getchar()` – Michi Jul 19 '16 at 22:50

1 Answers1

2

It works fine if I type in a < 5 characters userID, but when I type in a userID with more than 16 characters, the messages asking the user for a different userID are printed twice. Why's that?

  • When you enter more than 16 characters, then 17 characters are scanned into the string userID as you have not entered '\n', no null character will be placed at the end of the string userID.

  • and then,when you use strlen(userID), this invokes Undefined behavior as there is no terminating \0 character placed at the end. try doing this :

  • and when you restart the iteration the remaining extra integers are taken in.


Solution :

To avoid the above problems:

  1. Place a null character at the position index + 1 over each iteration.
  2. flush the stdin at the end of each iteration.

    do
    {
        printf("Please choose your user ID! (it must be between 6 - 16 charaters long)\n");
    
        for (int i = 0; i < 17; i++)
        {
            userID[i] = getchar();
            userID[i+1] = '\0'; //placing null character at the position index+1
    
            if(userID[i] == '\n')
            {
                userID[i] = '\0';
                break;
            }    
    
        }
    
        fflush(stdin); //flushing the extra input at the end of each iteration
    
    } while (strlen(userID) < 7  ||  (strlen(userID) > 16  &&  userID[16] != '\n'));
    

sample input :

more than 16 123456789
1123
123456789

sample output :

Please choose your user ID! (it must be between 6 - 16 charaters long)
more than 16 123456789
Please choose your user ID! (it must be between 6 - 16 charaters long)
1123
Please choose your user ID! (it must be between 6 - 16 charaters long)
123456789

Your acount info are:
    userID  : 123456789
    passcode: 

NOTE :

Avoid using fflussh(stdin) as it has portability issues as suggested by @JonathanLeffler in the comments. Also here's an explanatory post why not to use fflush(stdin) : click

Here's an implementation of your code avoiding the use of fflush(stdin)

do
{
    int i;

    printf("Please choose your user ID! (it must be between 6 - 16 charaters long)\n");

    for (i = 0; (userID[i] = getchar()); i++)
    {

        userID[i+1] = '\0'; //placing null character at the position index+1

        if(userID[i] == '\n')
        {
            userID[i] = '\0';
            break;
        }

        //if user enters more than 16 characters then,
        //scan in 17th character for `strlen(userID)` purpose and
        //consume all other characters using an integer `c`
        if( i > 17)
        {
            int c;

            while( ((c = getchar())!= '\n') && (c!=EOF) );
            break;
        }
    }

} while ( (strlen(userID) < 7)  ||  (strlen(userID) > 16 ) );
Community
  • 1
  • 1
Cherubim
  • 4,262
  • 3
  • 14
  • 33
  • Note the portability issues with [Using `fflush(stdin)`](http://stackoverflow.com/questions/2979209/using-fflushstdin) – Jonathan Leffler Jul 09 '16 at 05:24
  • @JonathanLeffler thanks for editing the post and sharing, what's best replacement for `fflush()` in this case... are there any? or any different approach must be adopted? – Cherubim Jul 09 '16 at 05:42
  • The portable replacement is `int c; while ((c = getchar()) != EOF && c != '\n') ;` (where the final semi-colon is an empty loop body, conventionally written on a line on its own, but I can't format that in a comment). Strictly, the main input loop should be written to check the input character for EOF before assigning to `userID[i]`, too — people can be irksome about such things (type control-Z a couple of times in a row on Windows; or control-D on Unix). – Jonathan Leffler Jul 09 '16 at 05:45
  • @JonathanLeffler I've provided an implementation avoiding `fflush(stdin)`, I have a small doubt... should I ***replace*** the first implementation with my new one as the answer (or) is it fine ***just to add*** the second implementation along with the previous one informing the OP why not to use it *(as I presently did)*... and thanks for providing info... though I've done a late edit :) – Cherubim Jul 16 '16 at 14:35