Avatar of shampouya
shampouya
 asked on

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

C

Avatar of undefined
Last Comment
sarabande

8/22/2022 - Mon
ozo

When you execute
  scanf("%s", secretNumberGuessString);
and enter 4 characters,
the terminating null overflows the allocated
  char secretNumberGuessString[4]
which caused undefined behavior
jmcg

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?
shampouya

ASKER
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?
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
ASKER CERTIFIED SOLUTION
ozo

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
jmcg

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.
shampouya

ASKER
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?
ozo

Did you also modify
char secretNumberGuessString[5];
?
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
jmcg

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.
ozo

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.
shampouya

ASKER
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?
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
ozo

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.
SOLUTION
jmcg

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
jmcg

Try using one of the other suggested methods for reading user input, such as 'getline' followed by 'sscanf'.
sarabande

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
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.