1

When I run the following code and give the inputs as "1 2 3 4 5", interestingly it gives the result 0 0 0 0 5. I tried several other numbers as inputs but only the last one is printed out as it is. I know I have to use int declaration when I want to get int inputs but I'm wondering why it gives the outputs as 0 except the last one?

int main(){

char a,b,c,d,e;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &e);
printf("%d %d %d %d %d", a, b, c, d, e);

return 0;

}

Example I/O: < 40 41 42 43 44

0 0 0 0 44

< 1 2 3 4 5

0 0 0 0 5

aucmd
  • 23
  • 3
  • 6
    The program has undefined behavior. – Vlad from Moscow May 02 '21 at 21:59
  • What do you mean by that? – aucmd May 02 '21 at 22:03
  • 2
    You are using incorrect conversion specifiers in scanf. So in this case the behavior of the function is not defined. – Vlad from Moscow May 02 '21 at 22:05
  • 2
    If your compiler isn't warning you about this, turn up the options until it does (`-Wall -Wextra` is a good start for gcc and clang) – Shawn May 02 '21 at 22:06
  • https://en.wikipedia.org/wiki/Undefined_behavior – yano May 02 '21 at 22:07
  • It doesn't answer my question. I know I'm making a mistake in the code. I just wanna know why it only gives the correct result of the last one and the other ones as 0. What is going on at the machine level, I wanna understand that. – aucmd May 02 '21 at 22:30
  • 1
    I'm guessing that `a`, `b`, `c`, `d`, and `e` are defined inside `main`, so they're created on the stack. Try defining them outside of `main`. What happens to your results? Try changing the order of definition (e.g. `c`, `d`, `a`, `e`, `b`) - how does this affect your results? Basically, **mess around with it** and develop an idea of what's going on. – Bob Jarvis - Слава Україні May 02 '21 at 22:58
  • Do you want to declare your variables as characters or integers? – Zois Tasoulas May 02 '21 at 23:28

3 Answers3

2

That program is ill formed and causes undefined behaviour. for the %d specifier, scanf expects a pointer to an int. While C does not define exact bit widths for char and int, for simplicity lets say a char is 8 bit and an int is 16 bit.

What probably happens is the following: Your five char variables a,b,c,d,e live in a consecutive block of stack memory like this:

Variable - e d c b a -
Memory 0x0 0x0 0x0 0x0 0x0 0x0 0x0

When scanf writes the first value (lets say its 1) to variable a, it assumes it is an 16 bit integer and the following happens:

An int with the value 1 (or 0x0001) is written to the memory where &a points. You are probably on a little-endian system, so the 0x0001 is represented as 0x0100 in memory (least significant byte first). You get:

Variable - e d c b a -
Memory 0x0 0x0 0x0 0x0 0x0 0x1 0x0

Then it writes the next value (say its 2, or 0x0002, represented in memory as 0x0200) to variable b:

Variable - e d c b a -
Memory 0x0 0x0 0x0 0x0 0x2 0x0 0x0

Note how it overwrites a again because it is writing an int (16 bits) instead of a char (8 bits), but b is only 8 bits wide.

Same happens for the remaining variables.

lulle2007200
  • 888
  • 9
  • 20
  • Thank you. This makes very sense. But I have a little question. Why it is sorted in the memory as e d c b a and not as a b c d e. If it would be like that, then it would print out the first one correct and other ones as zeros right? – aucmd May 02 '21 at 22:56
  • 2
    @aucmd: that would require intimate knowledge of how the compiler allocates space. As a working hypothesis, imagine what happens if the compiler builds a stack of variables, adding each new variable to the bottom (low address) of the stack as they are encountered. Then perhaps you'd get the arrangement suggested here. It's as good as anything else... – Bob Jarvis - Слава Україні May 02 '21 at 23:01
2

Let's take a simple example:

char c;
scanf("%d", &c);

scanf with a %d format requires an argument of type int*. It will try to read a value from stdin and, if successful will store an int value in the object to which the argument point.

So if c were of type int, this would be fine.

The standard says that if the type of the argument is incorrect for the format string, the behavior is undefined. That's all it says about it. That means that it might behave exactly the way you want it to (which is in a sense the worst possible outcome, because it means you have a bug that's difficult to diagnose, and might show up at the worst possible moment). Or it could cause your program to crash, or allow it to proceed with invalid data.

So what's actually likely to happen?

scanf, if it successfully reads an int value, will try to store that value in an int object located where c is. If char is 1 byte (which it is by definition) and int is 4 bytes (which is very common), then it will store 4 bytes of data to a 1-byte location. The remaining 3 bytes can clobber whatever is in the memory adjacent to c.

If those 3 bytes aren't allocated to anything, there might not be a visible symptom. If some other declared object is stored there, you could clobber that object. If some implementation-specific data is stored there (say, your function's return address), Bad Things Could Happen.

"Undefined behavior" means that your code is breaking the rules -- but it doesn't mean that the implementation has to do anything about it. The burden is on you, the programmer, to avoid breaking those rules, with or without the compiler's help.

Many compilers will, if invoked with the right options, warn you about this particular issue. Do not ignore those warnings.

You can say a lot about what's likely to happen in the presence of undefined behavior if you know something about the particular implementation you're using. And sometimes that can be useful. More often, though, especially if you're writing new code, your time is better spent fixing your code rather than figuring out all the myriad ways it can go wrong.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

What @VladfromMoscow means you trying to scanf() char with (%d) its only for decimal input not char you have 2 ways to fix this:

  1. change char to int
  2. change %d to %c
coderx64
  • 185
  • 1
  • 11
  • 1
    Using explicit `signed char` variables and `%hhd` is an option too. – Shawn May 02 '21 at 22:16
  • Given OP's examples, `%c` won't do the right thing - looks like they're wanting to read numbers, not characters. – Shawn May 02 '21 at 22:22
  • I know, I can fix the solution with that. But I don't understand the logic of why it only gives the right result of the last one. I have no intention to solve my problem I know how to solve it. I wanna understand the logic behind it (why it prints 0 and the last one as correct). – aucmd May 02 '21 at 22:26
  • Its undefined behavior its different from time to another its output depend on its location in the memory like 32 bit if you run your code its going to output regularly but its going to crash after the output but in 64 bit its just going to print last number then crash. the problem is you cant know what causes that only with debugging the specific code. int size is 4 byte in 64 bit and char is 1 so it could be some corrpted opcodes @aucmd – coderx64 May 02 '21 at 22:34
  • Actually, the outputs are not different from time to time. Whenever I try 5 numbers it gives the same result ( the first four 0 and that last one as correct), also my friend tried the code and got the same result. There must be a reason why it prints out 0 for the first four elements and the last one as correct. – aucmd May 02 '21 at 22:45
  • in easy way your trying to add 1 byte of opcodes to var need 4 bytes of opcode its going to crash the whole code read more about data types to know what i mean:here:https://www.tutorialspoint.com/cprogramming/c_data_types.htm @aucmd – coderx64 May 02 '21 at 22:48