0

Text editor : VS code

Compiler : minGW

I was needed to store a number between 1-10, so I thought why don't store it in a char variable because it takes only 1 byte.

so, look at the following code

char a = 3;
printf("%d", a);

OUTPUT

3

it worked fine

Then I decided to take value as input using scanf

Here's the code for that

char a;
printf("Enter number : ");
scanf("%d", &a);
printf("You entered : %d", a);

OUTPUT

Enter number : 9
You entered : 9

It worked fine too, but problem lies when I tried with two variables

Look at the the code below

char a = 2, b = 3;

printf("before scanf a : %d\n", a);
printf("before scanf b : %d\n", b);

printf("Enter new value of a : ");
scanf("%d", &a);

printf("after scanf a : %d\n", a);
printf("after scanf b : %d\n", b);

OUTPUT

before scanf a : 2
before scanf b : 3     
Enter new value of a : 9
after scanf a : 9
after scanf b : 0 //Notice value of b has changed to 0

Even though, I didn't even touched b's value. still it changed. . I tried a lot to figure it out but failed, I'm a beginner. I think it's something to do with stdin/out stream. please help me here..

Naman Vyas
  • 99
  • 5
  • 2
    `scanf("%d", &a);` is undefined behavior so anything can happen. [What is undefined behavior and how does it work?](https://software.codidact.com/q/277486) – Lundin Dec 10 '20 at 13:11
  • 1
    `scanf` is not `printf`... – Jabberwocky Dec 10 '20 at 13:17
  • The `char` passed to `printf` is promoted to `int` but a pointer is not promoted to a different type. – Weather Vane Dec 10 '20 at 13:19
  • try printing the address of `a` and `b` also in first `printf` statements, mostly they are getting allocated successive addresses, and your `b` values goes for a toss, since they are of 1 byte type but you are trying to extract and store 4 byte values – IrAM Dec 10 '20 at 13:19
  • 1
    @user3121023: The range of a `char` is implementation-defined. For a signed `char`, it must be at least −127 to +127, but it may be more. – Eric Postpischil Dec 10 '20 at 13:26
  • Memory overflow, "%d" in scanf requires a variable of size of an int, and you used a smaller, a char, then the value that would be stored in the variable overflowed to the stack – Matheus Rossi Saciotto Dec 11 '20 at 02:39

3 Answers3

1

The format specifier %d specifies int, not char. When you use this format specifier with printf and pass a char, this doesn't really matter because char gets implicitly promoted to an int.

But with scanf, you end up passing a char pointer (note that scanf uses &a instead of simply a) where scanf expects an int pointer. Hence scanf ends up copying an int into a char, which means that data spills over into the adjacent memory location(s). In your case, this adjacent location happens to be the variable b, thus the value of b gets overwritten with the MSB of the integer that is input, which in this case is 0.

PS: I should also mention the fact that you shouldn't really rely on this behaviour. What I mentioned above is the most likely explanation for your observation. This particular scenario falls under undefined behaviour so a different compiler, different hardware or a different run of the same program could theoretically have resulted in a drastically different outcome and it would still be perfectly legal! The proper solution is to use an int instead of a character. On a modern machine, you are not saving much anyway with these antics.

th33lf
  • 2,177
  • 11
  • 15
  • 1
    Probably should make mention of undefined behavior in there somewhere, maybe around adjacent memory locations. – ryyker Dec 10 '20 at 13:21
  • @rykker Thanks, fixed! – th33lf Dec 10 '20 at 13:25
  • Hey thanks a lot for your efforts, can you please tell me that what is the alternative way of storing number in least memory storage. – Naman Vyas Dec 10 '20 at 13:37
  • *this doesn't really matter because char gets implicitly promoted to an int* There are probably some cases on systems where `char` is unsigned where it does matter. – Andrew Henle Dec 10 '20 at 13:39
  • what does it mean that **"char gets promoted to int"** – Naman Vyas Dec 10 '20 at 13:44
  • @anonymous you could use the right format specifier (`%hhd`) or use `int a` instead of `char`. On promotion rules, please take a look at this page: https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules – th33lf Dec 10 '20 at 13:55
  • @AndrewHenle Perhaps, but only if you try storing negative numbers in an unsigned char. – th33lf Dec 10 '20 at 14:01
1

scanf("%d", &a); instructs scanf to read a decimal numeral and assign it to an int pointed to by &a. Since &a points to a char, not an int, the behavior is not defined by the C standard.

Proper code is to declare a as a signed char and use scanf("%hhd", &a);. (Technically, there is no conversion specifier to assign to a char.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

What's happening is that your program thinks a is of type int. Since the size of an int is 4 bytes, if you do:

char a = 2, b = 3, c = 4, d = 5, e = 6;

scanf("%d", &a) will corrupt the values of b, c, d (but will not corrupt the value of e, because e is the 5th byte, and the size of int is 4), because scanf thinks that you've passed a pointer to an int, not to a char.

uncoder21
  • 11
  • 3
  • 1
    A compiler will not necessarily allocate `b`, `c`, and `d` adjacent to `a`, nor will it necessarily allocate `e` away from `a`. Further, if `b`, `c`, and `d` are adjacent to `a`, they are not necessarily on the side that bytes beyond `a` would spill to. And the compiler may optimize them away completely if they are unused or store them in a register if it has sufficient register space available. – Eric Postpischil Dec 10 '20 at 13:23
  • Keep it simple. There's absolute no reason to involve structs and padding, just read the character correctly. Or read an int if you want an int. – Lundin Dec 10 '20 at 13:47
  • It is still UB. with `struct`. – chux - Reinstate Monica Dec 10 '20 at 15:33