Lavya
Lavya

Reputation: 1601

can you give me a test case where these two c programs will produce different outputs?

program 1:

#include <stdio.h>

#define MAXSTR 100

int main() 
{
    char ch;
    char str[MAXSTR]; 
    char line[MAXSTR];
 
    scanf("%c", &ch);          // Just read character
    scanf("\n");              // Consume newline
    scanf("%s", str);         // Read string
    scanf("\n");              // Consume newline
    scanf("%[^\n]%*c", line);  // Read full line

    printf("%c\n", ch);
    printf("%s\n", str);
    printf("%s\n", line);

    return 0;
}

Program 2:

#include <stdio.h>

#define MAXSTR 100

int main() 
{
    char ch;
    char str[MAXSTR]; 
    char line[MAXSTR];
 
    scanf("%c\n", &ch);
    scanf("%s\n", str);
    scanf("%[^\n]%*c", line);

    printf("%c\n", ch);
    printf("%s\n", str);
    printf("%s\n", line);

    return 0;
}

Upvotes: 0

Views: 96

Answers (2)

chux
chux

Reputation: 154174

In addition to trouble of overlong input for str[] and line[] as well identified by @Eric Postpischil, both codes have trouble when input stops early.

Reading from stdin (as scanf() does) may end with an end-of-file (think re-directed input) or rarely an input error.

Should overall input consist of "x\n" then end-of-file, both scanf("%s", str); and scanf("%s\n", str); return with str[] unchanged as there is nothing left to read after the prior scanf()s. Since str[] is not initialized, printf("%s\n", str); attempts to print a supposed string that is not certainly null character terminated. This leads to undefined behavior and that is not certainly the same between the 2 programs nor even between 2 runs of the same program.

Same issue applies for line[].

Best to check the return value of input functions.

Research fgets() for reading a line.


For an esoteric difference between the 2, consider an input stream that has an input error.

X Enter (Nicely read by both programs)
Y input error *1 Enter
Z Enter

Program 1, with scanf("%s", str); returns on the error and the following scanf("\n"); consumes the Enter after the error. scanf("%[^\n]%*c", line); reads the Z Enter.

Program 2, with scanf("%s\n", str); returns on the error and leaves Enter in stdin. Thus blocking the following scanf("%[^\n]%*c", line); from reading anything. Enter Z Enter remains unread.

Note: Once the end-of-file flag is set, this and subsequent input attempts return EOF. This stickiness does not apply to an input error when the error is transitory.

This rare type of error is difficult to test.


*1 Think of reading from a keyboard with a serial interface that had a parity error for one character.

Upvotes: 2

Eric Postpischil
Eric Postpischil

Reputation: 223776

If the input causes the scanf to exceed the bounds of str or line, then the behavior is not defined by the C standard, and then the behavior may depend on arbitrary details of the programs and the C implementation, such as the fact they contain different strings ("%s" and "\n" in one, "%s\n" in another) and different code.

In the absence of such undefined behavior, the differences in the program are solely in the fact that one has:

scanf("%c", &ch);
scanf("\n");
scanf("%s", str);
scanf("\n");

and the other has:

scanf("%c\n", &ch);
scanf("%s\n", str);

Both these sequences of code nominally ask scanf to match (and consume) the same input sequences. scanf has three effects:

  1. It reads and consumes input.
  2. It assigns results in locations pointed to by arguments.
  3. It returns a value.

Since this code ignores the return value of scanf, there is no difference due to 3.

Since this code asks scanf to match the same input sequences, there is no difference due to the individual input sequences. There can only be differences due to combined input sequences. In general, if we have some conversion specifications A and B, then scanf("AB", …); processes B if and only if processing of A completes, whereas scanf("A", …); scanf("B", …); always processes B after attempting A.

Therefore, there is a difference between the two programs only if the initial conversion does not complete.

This can occur with some conversion specifications that require particular formats. For example, with %d%c, if the input is -x, then the %d will fail because -x is not a valid match for %d. With scanf("%d%c", …);, no assignment will be made for %c since %d failed. With scanf("%d", …); scanf("%c", …);, an assignment will be made for %c. However, for the conversions in the programs in the question, this does not occur. %c matches any character and will never have a matching failure. It can fail only if there is an end-of-file or read error, in which case the \n is not executed (in one program) or also fails and has no effect (in the other program) because the end-of-file or read error still exists.

Similarly, %s matches any sequence of non-white-space characters and will never have a matching failure.

So, I see no input that will cause different observable behavior in the programs, other than overrunning the buffers as stated above.

(Note: Contrary to comments in the program, scanf("\n"); does not merely consume a newline character. It consumes all white-space characters.)

Upvotes: 3

Related Questions