• C

Why do I lose my variable values at each iteration of my loop in my C program?

The p2ConsecutivePasses variable turns to 0 at each iteration of the main loop in the code below, even after it was incremented to 1 in a previous iteration of the loop. What is setting p2ConsecutivePasses to 0 repeatedly during this loop? The other variable, p1ConsecutivePasses, does not have this problem

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

int main() {

    int authorizedPlayerNumber = 0, secretNumber = 0, playerSelfID= 0;
    int p1PassesLeft=3, p2PassesLeft=3;
    int secretNumberGuess = 1;
    int p1ConsecutivePasses = 0;
    int p2ConsecutivePasses = 0;
    char secretNumberGuessString[4] = {0};
    srand(time(NULL));
    authorizedPlayerNumber = 1 + rand() % 2; /* Random number is generated */
    srand(time(NULL));
    secretNumber = 0 + rand() % 10; /* Random number is generated */
    
    while (secretNumber!=secretNumberGuess)
    {
        printf("\nIt's player's %d turn", authorizedPlayerNumber);//playerID prompt
        printf("\nPlayer Number? ");
        scanf("%d", &playerSelfID);
        
        while (playerSelfID != 1 && playerSelfID != 2) { //playerID validity test
            printf("\n%d is not a valid player number. \nPlayer number? ", playerSelfID);
            scanf("%d", &playerSelfID);
        }
        while (authorizedPlayerNumber != playerSelfID)
        {
            printf("\nWait your turn."); //alert and playerID reprompt
            printf("\nIt's player's %d turn.", authorizedPlayerNumber);
            printf("\nPlayer number? ");
            scanf("%d", &playerSelfID);
        }
        
        printf("Enter Your Guess, 0 - 10 or pass: ");
        scanf("%s", secretNumberGuessString);
        
        secretNumberGuess = atoi(secretNumberGuessString);  //converts string to int
        
        if (strcmp(secretNumberGuessString, "pass") == 0){ //test if user entered pass as his guess
            if (authorizedPlayerNumber == 1){
                if(p1PassesLeft<1){
                    printf("\nPlayer 1, you cannot pass because you have no passes left.");
                    goto endOfLoop;
                    //skip to end of iteration of loop, w/out changing p-turn or incrementing pass variables
                }
                if(p1ConsecutivePasses>=2){
                    printf("\nPlayer 1, you cannot pass more than 2 times in a row.");
                    goto endOfLoop;
                }
                p1PassesLeft--;
                p1ConsecutivePasses++;
                printf("Player 1 has %d passes remaining.\n", p1PassesLeft);
            }
            else if(authorizedPlayerNumber == 2){
                if(p2PassesLeft<1){
                    printf("\nPlayer 2, you cannot pass because you have no passes left.");
                    goto endOfLoop;
                }
                if(p2ConsecutivePasses>=2){
                    printf("\nPlayer 2, you cannot pass more than 2 times in a row.");
                    goto endOfLoop;
                }
                p2PassesLeft--;
                p2ConsecutivePasses++;
                printf("Player 2 has %d passes remaining.\n", p2PassesLeft);
            }
        }
        else if (secretNumberGuess <= 10 && secretNumberGuess >= 0 ){
            if (authorizedPlayerNumber == 1){
                p1ConsecutivePasses = 0; //reset player2's consecutive passes
            }
            if (authorizedPlayerNumber == 2){
                p2ConsecutivePasses = 0; //reset player1's consecutive passes
            }
            if(secretNumberGuess < secretNumber)
                printf("Your guess was too low.\n ");
            else if(secretNumberGuess > secretNumber)
                printf("Your guess was too high.\n ");
            else
                printf("Good job Player %d! You are correct.\n", authorizedPlayerNumber);
        }
        else{
            printf("You did not enter a valid number. You lose your turn.\n ");
        }
        if (authorizedPlayerNumber == 1) { //could have added a boolean turnComplete variable to this test
            authorizedPlayerNumber = 2;
        }
        else if (authorizedPlayerNumber == 2) {
            authorizedPlayerNumber = 1;
        }
        
    endOfLoop: ; //players who exceed 2 passes in a row get sent here
    }
    return 0;
}

Open in new window

shampouyaAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ozoCommented:
When you execute
  scanf("%s", secretNumberGuessString);
and enter 4 characters,
the terminating null overflows the allocated
  char secretNumberGuessString[4]
which caused undefined behavior
jmcgOwnerCommented:
You should initialize secretNumberGuess to a value that is outside the range of possible secretNumber values or you will sometimes skip the main loop entirely.

On what basis are you thinking that p2ConsecutivePasses gets reset to 0 on every iteration?
shampouyaAuthor Commented:
On my x-code compiler, for some reason, p2ConsecutivePasses gets reset to 0 every time scanf("%s", secretNumberGuessString) executes. Why is secretNumberGuessString[4] being overflowed, as ozo stated? How do I prevent that?
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

ozoCommented:
char secretNumberGuessString[5];
...
scanf("%4s%*[^\n ]", secretNumberGuessString);

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
jmcgOwnerCommented:
If the question is not deleted, we can point to it as an example of the unsafe usage of scanf.

String overflows with scanf

In any case, ozo identified the reason for the problem being asked about, ozo should get credit.
shampouyaAuthor Commented:
I tried the scanf("%4s%*[^\n ]", secretNumberGuessString) that ozo recommended, but I still got the same problem with p2ConsecutivePasses resetting to 0 erroneously. I noticed that this happened in the x-code compiler, but not the gcc compiler. Any idea why that is?
ozoCommented:
Did you also modify
char secretNumberGuessString[5];
?
jmcgOwnerCommented:
Different compilers may lay out the variables in a different order, leading to different behavior when a program contains opportunities for buffer overflow, uninitialized variables, null pointers, etc.
ozoCommented:
Undefined Behavior means behavior for which [the language standard] imposes no requirements
Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment to terminating a translation or execution.
If a "shall" or "shall not" requirement is violated, the behavior is undefined.
s Matches a sequence of non-white-space characters
... the corresponding argument shall be a
pointer to the initial element of a character array large enough to accept the
sequence and a terminating null character, which will be added automatically.
shampouyaAuthor Commented:
Yep the char secretNumberGuessString[5]; by itself seemed to do the trick. Just changing the 4 to a 5 in my original code made it work, why is hat?
ozoCommented:
Changing [4] to [5] by itself may resolve the problem when a user enters exactly 4 characters, but it won't resolve the undefined behavior if a user enters 5 characters.
jmcgOwnerCommented:
Character strings in C are terminated by a NUL (0) character. It seemed like a good idea at the time, but has been the source of many problems.

So, when you enter "pass" and then hit return, the scanf routine deposits the following five characters at the address you gave it:

'p' 'a' 's' 's' '\0'

When secretNumberGuessString was defined to contain only 4 characters, that '\0' character gets stored at a location that is "undefined" by the language standard, which is why Ozo has been quoting you all the standard's verbiage about what is meant by "undefined behavior". It seems the x-code compiler was storing that '\0' in the storage location reserved for the variable p2ConsecutivePasses.
jmcgOwnerCommented:
Try using one of the other suggested methods for reading user input, such as 'getline' followed by 'sscanf'.
sarabandeCommented:
but it won't resolve the undefined behavior if a user enters 5 characters
you may use a generously sized input buffer to avoid such issues.

char input[256] = { '\0' };

Open in new window


additionally add the maximum number of characters allowed to scanf format string as shown by ozo.

or you read char by char to be absolutely safe

char input[256] = { '\0' };
int n = 0;
while (n < 255 && (input[n] = getchar()) != '\n')
     ++n;
input[n] = '\0';

Open in new window


the above loop also allows to check (and possibly translate) each single character.

Sara
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.