Rudalf
Rudalf

Reputation: 21

Not sure why while loop works different when using scanf

I'm new in the journey of learning C and to my knowledge, I knew that while loop would run forever if someone gives condition such as while (1 == 1) it would become a forever loop.

But While reading from a book I noticed a code such as

#include<stdio.h>

int main() {

    char a;
    int started = 0;
    puts("Scanning\n");
    while (scanf("%s[^\n]",&a) == 1)
    {
        if (a >= 65 && a <= 90)
        {
            printf("Char is capital\n");
        }
        else
        {
            printf("Not cap.\n");
        }

    }
    puts("Done\n");
    return 0;


}

Command line => char.exe < file.txt

It takes input from stdin but scanf returns the number of argument i.e 1 so the condition should become while (1 == 1)

But why this is not a forever loop and exists after reading the file?

Thanks

Upvotes: 1

Views: 394

Answers (5)

Mohammad Azim
Mohammad Azim

Reputation: 2973

&a indicates that you are trying to read a whole line in a stack variable.

But why this is not a forever loop and exists after reading the file?

Trying to read a file in a stack variable will cause stack smashing and your program would crash.

Look for different storage types and memory management in C on internet. a is a character variable allocated on the program stack and is only suppose to store one character. If you take it's address and try to put more data then it will corrupt adjacent stack memory/variables until OS detects that and kills your program. Your format indicates it will read a whole line i.e. until new line. scanf will still return 1 but it will try to store that line in the address of a.

Did you run it like:

cat <your file>| ./a.out

Allocate a big enough buffer like char a[4096]; and pass a as

while(scanf("%s[^\n]", a) == 1)

Note now a is an array now and does not need a & in front. Your loop will still exit but on EOF when the file finishes. Try to use a file smaller than 4 Kb side for the worst case if new line does not appear until 4Kb.

Provisions for reading a bigger file/lines seem out of scope of your experiment.

Working code

#include<stdio.h>

int main() {

char a[4096];
int started = 0;
puts("Scanning\n");
while (scanf("%s[^\n]", a) == 1)
{
    if (a >= 65 && a <= 90)
    {
        printf("Char is capital\n");
    }
    else
    {
        printf("Not cap.\n");
    }

}
puts("Done\n");
return 0;
}

Upvotes: 0

bruno
bruno

Reputation: 32596

In

char a;
...
while (scanf("%s[^\n]",&a) == 1)

your format is invalid because dedicated to a string while you want to read only a char, so you will write out of the char at least for the ending null char producing an undefined behavior

do

char a;
...
while (scanf(" %c",&a) == 1)

and notice the space in the format, remove it if you want to manage all the characters

In

 if (a >= 65 && a <= 90)

it is wrong to use ASCII code, this is not readable and not compatible with non ASCII

you can do

if (a >= 'A' && a <= 'Z')

or better use isupper (<ctype.h>) because it is not guaranty than the uppercase letters are consecutive

Your printf can be replaced by puts removing the \n in the string, or a fputs, is is useless to have printf to print a simple string (without % and arg)

But why this is not a forever loop and exists after reading the file?

scanf does not return 1 on EOF, so the loop stops on EOF, about scanf family :

These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.

Upvotes: 2

Amankumar Singh
Amankumar Singh

Reputation: 53

When you enter a letter the condition (1==1) becomes true and the body of while loop is iterated.

After iteration of while loop it goes for next letter. scanf waits till you enter next character.

Again condition (1==1) is satisfied.

This loop is an indefinite loop and you could go on entering letters and you would never reach the puts statement.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 181714

But why this is not a forever loop and exists after reading the file?

It will loop indefinitely only if scanf returns 1 every time it is called. The loop terminates the first time that function returns a different result.*

scanf's return value conveys the number of input fields successfully scanned and assigned, and under some circumstances also conveys information about I/O errors. In particular, scanf will return the value of macro EOF if it reaches the end of the input before matching any fields. EOF is unequal to 1.


*Provided that the program's behavior is well defined. Your program's is not, for reasons described in comments and another answer.

Upvotes: 1

akshay kishore
akshay kishore

Reputation: 1037

The function scanf returns the following value:

  • >0 The number of items converted and assigned successfully.
  • 0 — No item was assigned.

  • <0 — Read error encountered or end-of-file (EOF) reached before any assignment was made.

Upvotes: 0

Related Questions