Control flow with while loops and if statements - c

I'm having trouble understanding the control flow within a segment of my code. The code is written to take a startword (i.e cat) and an endword (i.e dog) change 1 letter of the original word at a time and reach the end word - checking against a dictionary of real words. If a non-real word is reached, i.e cat --> dat, it should break out of the loop and try changing a different letter.
while (strcmp (startword, endword) != FALSE)
{
change_letter(startword, endword, i++);
if ((check_dictionary(dictionary, startword)) == FALSE)
{
printf("%s --> ", startword);
printf("Bad route\n");
break;
}
if ((check_dictionary(dictionary, startword)) == TRUE)
{
printf("%s --> ", startword);
}
}
change_letter will just do startword[i] = endword[i], where i is initialized at 0 (for the first letter, and moves through the letters as i++ appears.
check dictionary compares the word against the dictionary, if the word is found a 1 is returned. (#define TRUE = 1, #define FALSE = 0).
Right now it will print (if using cat and dog as the words)
cat --> dat --> Bad route and thats all.
To my understanding it should never leave the top while loop whilst the two words are not equal.

while (strcmp (startword, endword))
{
change_letter(startword, endword, i++);
if (!check_dictionary(dictionary, startword))
{
printf("%s --> ", startword);
printf("Bad route\n");
}
else (check_dictionary(dictionary, startword))
{
printf("%s --> ", startword);
}
}
Modified your code a bit. Removed break, as it breaks control flow from loop and control is on the next statement after loop's }. That was your issue. Not sure if it works now, though, as it depends on your check_dictionary implementation.
Additionally, comparison == TRUE is useless. You can endlessly write check_dictionary(dictionary, startword) == TRUE == TRUE == TRUE == TRUE, the meaning will be all the same. Same as in while loop. It makes your code less understandable and is generally a bad practice.
After all, I removed second function call to make the function return value being evaluated only once.
Also, try to control your code readability by keeping same coding style (bracers!). It improves your code's readability.
Sorry for being boring.

Related

Variable loses its value C

I'm working on this project where a user has to guess a word (wordToGuess) and he has a number of attempts.
The problem is that the variable "wordToGuess" loses its value when the code arrives in the point marked ("HERE LOSES ITS VALUE). I don't know how to solve this problem, I've tried in many ways. Thank u for your help!
(checkExistence is a function that checks if the word is present in the dictionary)
void newGame(node* head){
char wordToGuess[10];
char attempt[10];
int numberOfAttempts = 0;
if (scanf("%s" , wordToGuess) != 1){
printf("error1");
}
getchar();
if (scanf("%d", &numberOfAttempts) != 1){
printf("error2");
}
getchar();
while(numberOfAttempts > 0){
if (scanf("%s", attempt) != EOF){
if (attempt[0] != '+'){
if (checkExistence(head, attempt) == false){
printf("not_exists\n");
}else{
if (strcmp(wordToGuess, attempt) == 0){
printf("ok\n");
return;
}else{
//code
numberOfAttempts--;
}
}
}else{
if (attempt[0] == '+' && attempt[1] == 's'){
//HERE LOSES ITS VALUE
}else if (attempt[0] == '+' && attempt[1] == 'i'){
//other code
}
}
}else{
printf("ko");
return;
}
}
return;
}
Here a test case:
2rj9R (wordToGuess)
18 (numerAttemps)
DP3wc (attempt)
7PGPU (attempt)
2rz9R (attempt)
+print_list (from this point I lose the value of wordToGuess)
2rj9R (attempt)
As the others have point, you're probably causing a buffer overflow in your attempt buffer which overwrites your wordToGuess buffer since your attempt and wordToGuess buffer is stored like this in your memory:
<attempt buffer> | <word To Guess>
You have two possible fixes for this (as the comments have said...):
A little fix would be to set a limit of how many characters should be read from stdin to scanf like this:
scanf("%9s" , wordToGuess) // you need 9, because your buffer can store up to
// 10 bytes but don't forget that `scanf` is also
// addinng `\0` for you!
and don't forget to flush the rest of the user input if you want that the user should be only able to insert at most 9 characters!
Increase the buffer size of your attempt (and wordToGuess) buffer but also add those read-limits for scanf which is described in the first point.
At the indicated point of the code where wordToGuess appears to lose its value, it is a dead variable. If you're looking at optimized code in a debugger, you may find that the variable doesn't exist there any more.
At a given point in a program, a dead variable is one which is never used past that point. All control flows out of that point reach the termination of that code, without ever using the variable again. Simple example:
{
int x = 3;
// x is live here: there is a next reference
printf("%d\n", x);
// x is now dead: it is not referenced after the above use
printf("foo\n");
}
In the generated code, the compiler may arrange to re-use the resources tied to a dead variable as soon as it is dead: give its register and memory location to something else.
In the debugger, if we put a breakpoint on the printf("foo\n") and try to examine x, we might get a strange result.
To have the best chance of seeing the expected result (that x still exists, and is retaining its most recent value), we have to compile with optimizations disabled.

How can I execute an 'if' statement only once per iteration in a for loop?

Assuming there are no duplicate words in either list, I would like to compare the words of listA with the words in listB.
If there is a match, I want to print the word that matches and compare the next 'n' words in listB to see if there is a match.
Likewise, if there is no match, (i.e once I reach the last word in listA), I want to print the word that could not be found and compare the next 'n' words in listB to see if there is a match.
I am stuck on how I should implement statements (if, break, continue) in my for loop so that it meets the specifications listed above.
When I run the code below, it only prints the instance in which there is a match, but it does not print anything at all if there is no match.
alineno & blineno refer to current line number in the arrays aline & bline where the words are stored
// index through listA
for(i = 0; i < alineno; i++){
// index through all the words in listB
for(j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
}
continue;
if(strcmp(aline[strlen(aline[0])-1], bline[j]) != 0){
printf("%s is not in the list!", bline[j]);
}
}
}
Input:
listA: Aardvark,Cat,Bear,Dog
listB: Cat,Badger
Expected Output:
Cat is in the list!
Badger is not in the list!
Actual Output:
Cat is in the list!
EDIT:
I understand that my continue statement is the reason why the second condition is not being checked. Removing it would print a word is / is not in the list 'j' amount of times, which is not my desired output. In other words, I would appreciate guidance on how I should implement such statements in order to meet the specifications.
My suggestion is that you change the loops, so you have the loop over "listB" as the outer loop, and iterate over "listA" in the inner loop.
Then you can easily set a flag in the inner loop, and break out of it when a match is found. In the outer loop you check this flag to decide what to print.
In pseudo code perhaps something like this
for (to_find in listB)
{
found_flag = false;
for (animal in listA)
{
if (to_find == animal)
{
found_flag = true;
break;
}
}
if (found_flag)
printf("Animal found");
else
printf("Animal not found");
}
Your continue is always executed; you will never reach your second if.
The best way to do this is probably binary search or a hash table, depending on the amount of data. That being said, the code could be improved in the following way:
for(int i = 0; i < alineno; i++)
{
int j;
for(j = 0; j < blineno; j++)
{
if(strcmp(aline[i], bline[j]) == 0)
break;
}
if(j == blineno)
printf("%s is not in the list!", aline[i]);
else
printf("%s is in the list!", bline[j]);
}
Note: aline[i] not bline[i] in the printf. bline[i] would be a potential array out of bounds bug, if alineno and blineno are allowed to have different lengths.
First, use goto, like this:
void something(void) {
// index through listA
for(int i = 0; i < alineno; i++){
// index through all the words in listB
for(int j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
goto doneAnimal;
}
}
printf("%s is not in the list!", bline[i]);
doneAnimal: ;
}
}
Second; to avoid the risk of "goto is bad" nonsense (see Historical Note below), make the code harder to read by splitting it into 2 different functions, so that you can convert the goto into a return, like this:
void something(void) {
// index through listA
for(int i = 0; i < alineno; i++){
doAnimal(i, blineno);
}
}
void doAnimal(int i, int blineno) {
for(int j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
return;
}
}
printf("%s is not in the list!", bline[i]);
}
Historical Note
Once upon a time higher level languages (like assembly language) did not have structured programming features (do, while, break, continue, switch, ...). Instead programmers would write code using goto, like (e.g.) "if(x < MAX) goto loopStart; instead of a "} while(x < MAX);.
To encourage the adoption of structured programming features, in 1968 Edsger W. Dijkstra wrote a letter to the editor of ACM entitled "go to statement considered harmful". This letter had the desired effect - structured programming features (do, while, break, continue, switch, ...) were adopted in all major languages.
However; it also had one unintended side-effect - the letter was a little too effective; and ignorant people (that failed to read the letter or understand its context) started becoming zealots, making their code worse (for cases where the new structured language features aren't enough) to avoid goto without understanding why, and encouraging other people to make their code worse without understanding why.
Examples of this include complicating code by introducing extra variables purely for the sake of avoiding a simpler goto, and/or complicating code to introduce extra branches purely for the sake of avoiding a simpler goto.
Later (in conversations with Donald E. Knuth); Dijkstra himself said "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"
Sadly; once ignorance begins to spread common sense is fighting a losing battle.

While loop affected by strcmp if statement. Why is this?

Basically, I am having issues with the while loop below in part of my program.
Here is the section of code I'm having issue with:
char *nameOfTheCommand;
char *arrayArgs[500];
//track for redirection. If set, gives position of the file name. Else it equals zero
int redirectionCheck=0;
arrayArgs[0]=token;
int i;
i=0;
//While still arguments to take in, do this
while(arrayArgs[i]!=NULL)
{
i++;
arrayArgs[i]=strtok(NULL, " \n");
if(strcmp(arrayArgs[i], "<")==0)
{
redirectionCheck=i;
}
}
All I want the code to do is loop through strtok and set it equal to arrayArgs[i]. If the strtok happens to pull out a "<" symbol, then I want redirection to be set to i.
This seems simple enough. Yet, if I include the if statement in the while loop, it seem that the while loop exits and no code after that executes. I put a printf statement after the while loop and it won't print anything, that is how I know that it is like the while loop just stops everything else running after it immidiately.
Yet, if I don't include the if statment, my code runs fine.
Can someone please explain to me why this if statment seems to be causing my while loop to not act correctly? As in, it seems the while loop just wont execute stuff after it if I include it. Thanks for any information.
In the final iteration of the loop, you pass NULL to strcmp. This can be avoided by rearranging the loop:
i = 1;
// read subsequent tokens
while((arrayArgs[i] = strtok(NULL, " \n")) != NULL)
{
if(strcmp(arrayArgs[i], "<") == 0)
{
redirectionCheck = i;
}
i++;
}
but I would also add a check on the value of i.

How could I rewrite this selection structure with only if statements?

A problem I have asks to create a program, where a user is to enter an integer value (between 1 and 4) and depending on the input, a specific output is to be generated.
It must also provide an error to the user when the value entered is not between 1 and 4.
I managed to write it using if and else statements, however, I'm wanting to know if it possible to write this using only if statements.
there is a lot of ways to skin this cat! While not using else statements is one of them, setting such a constraint seems unnecessary and makes code less readable and doesn't really do anything for else is equivalent to if ( ! resp_1 == 1 ||... ) also notice that you were redundant in your if statements having a check for if resp_1 was equal to 1 or 2 or 3 or 4 before again checking individually and printing your text.
my suggestion would be to use a switch, it is readable, and quick to implement
switch(resp_1){
case(1): /*print*/ break;
case(2): /*print*/ break;
case(3): /*print*/ break;
case(4): /*print*/ break;
defualt: /*Print if not 1,2,3,4*/
}
If you must use if statements only, then just chain them all.
if (resp_1 == 1) {
// Print output for 1
} else if (resp_1 == 2) {
// Print output for 2
} else if (resp_1 == 3) {
// Print output for 3
} else if (resp_1 == 4) {
// Print output for 4
} else {
// Print error
}
An if-else statement executes (exactly) one branch. And here each else branch is another if-else statement. So, only one branch will be executed; either one of the branches for valid input, or the last catch-all else branch.
And while it's possible to use a flag and write this without a single else clause, IMO it will be far less clear what you intended to happen. When writing code, always go for the option that best captures your intent. Two weeks down the line, if the code requires clarification, it'll be all that easier to explain it to yourself first.

Something strange with if() in c

This is for a school project I'm working on, it's just a small part of the code but for some reason the program doesn't seem to go inside the if() no matter what the input is. I've tried anything and everything I know of (also used the isalpha() function) but it just won't run the commands inside the if().
do
{
flag=1;
gets(input.number);
printf("\n%s\n",input.number);
for(i=0;i<strlen(input.number);i++)
{
printf("yolo1\n"); //debug
if(input.number[i]<48 || input.number[i]>57) //PROBLEM HERE
{
printf("\nyolo2\n"); //debug
flag=-1;
break;
}
}
if(strlen(input.number)<1000 || strlen(input.number)>9999 || flag==-1) printf("\nINVALID INPUT\n\nARI8MOS: ");
}while(strlen(input.number)<1000 || strlen(input.number)>9999 || flag==-1);
Can you guys help me out here??? I've been staring and the code for the better part of 3 days now....
I presume you declared char input.number[].
Your condition in if says that you only want to get into its body if the character is NOT a digit. This is somehow contradictory to the name input.number of the variable (but perhaps you are just checking for incorrect characters here...)
To better see what is happening with the condition, you can choose to print the values of its components, like this:
printf("%c[%d]", input.number[i], input.number[i]);
printf("%d, %d, %d\n", input.number[i]<48 , input.number[i]>57, input.number[i]<48 || input.number[i]>57);
(you will se a 0 for false and 1 for true)
BTW: You could write the same condition in a more readable manner, using char constants like this:
input.number[i]<'0' || input.number[i]>'9')

Resources