strange behavior of scanf for short int

the code is as follows:

#include <stdio.h>
main()
{
    int m=123;
    int n = 1234;
    short int a;
    a=~0;
    if((a>>5)!=a){
        printf("Logical Shift\n");
        m=0;
    }
    else{
        printf("Arithmetic Shift\n");
        m=1;
    }
    scanf("%d",&a);
    printf("%d\n", m);
}

after the line scanf("%d",&a); the value of m becomes 0.

I know it may be caused by the scanf: a's type is short and the input's type is int. But How can this affect the value of m ?

Thanks a lot !

Answers


The most likely reason for m being 0 in your snippet is because you assign m to have this value in the body of your if-statement, but since the code contains undefined behavior no one can say that for sure.


The likely story about passing a short* when scanf expects an int*

Assuming sizeof(short) = 2 and sizeof(int) == 4.

When entering your main function the stack on which the variables reside would normally look something like the below:

  _
 |short int (a)   : scanf will try to read an int (4 bytes).
 |_ 2 bytes       : This part of memory will most
 |int       (n)   : likely be overwritten
 |                :..
 |
 |_ 4 bytes
 |int       (m)
 |
 |
 |_ 4 bytes

When you read a %d (ie. an int) into the variable a that shouldn't affect variable m, though n will most likely have parts of it overwritten.


Undefined Behavior

Though it's all a guessing game since you are invoking what we normally refer to as "undefined behavior" when using your scanf statement.

Everything the standard doesn't guarantee is UB, and the result could be anything. Maybe you will write data to another segment that is part of a different variable, or maybe you might make the universe implode.

Nobody can guarantee that we will live to see another day when UB is present.


How to read a short int using scanf

Use %hd, and be sure to pass it a short*.. we've had enough of UB for one night!


Assuming that int and short are four- and two-byte integers, respectively, on your platform (which is a likely assumption, but not guaranteed by the standard), you're asking scanf to read in an integer and store it in four bytes: the two bytes of b, and whatever two bytes follow it in memory. (Well, technically this is undefined behavior, and no specific behavior is guaranteed; but that's what it's likely to do.) Apparently your compiler is using the two bytes after b as the first two bytes of m. Which is a bit surprising — I certainly wouldn't expect b and m to be adjacent, and it rather implies that your compiler isn't aligning shorts and ints to the beginning of four-byte blocks — but perfectly legal.

You can see better what's going on if you add

printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m);

which will show you where a and m are stored, relative to each other. (Just as a test, I mean. You wouldn't want that in "real" code.)


You are correct, %d expects and writes an int. If you enter a value less than 65535, it fits in the bytes outside short, so you see 0 when you print a back. I tried reading a short and printing it back; I entered 65536123, and got 123, which makes perfect sense (65536 occupies precisely 16 bits; you see the remaining 123 through the two bytes of the short). This behavior is dangerous, because the other two bytes of the short end up in a "variable next door" to the short, which is very, very bad. I hope this should convince you not to do it.

P.S. To read a short with scanf, declare a temporary int variable, read the value into it using scanf, and then cast it to short.


You're invoking Undefined Behavior when passing a pointer to a non-int to scanf's %d.

Likely, the compiler introduces padding bytes for alignment purposes and the values get stored in the padding bytes and not the "useful" bytes.

However, the compiler is free to do anything from raise a segfault / access violation to invoke nasal demons.


If you had actually used variable n, then it would probably have been the one that got clobbered, rather than m. Since you didn't use n, the compiler optimized it away, and that means that it was m that got clobbered by scanf() writing 4 bytes (because it was told that it got a pointer to a (4-byte) integer) instead of the 2 bytes. This depends on quite a lot of details of your hardware, such as endian-ness and alignment (if int had to be aligned on a 4-byte boundary, you wouldn't see the problem; I guess you are on an Intel machine rather than, say, PowerPC or SPARC).

Don't fib to your compiler - even accidentally. It will get its own back.


Need Your Help

Do python list comprehensions get converted to pure C?

python list-comprehension

I've been told multiple times that Python list comprehensions are better than nested for, if simply because they are converted to pure C and compiled. However, I cannot find any documentation to su...