Marcin Król
Marcin Król

Reputation: 1654

Strange C function returned variable type behaviour

I met a behaviour I don't understand. I want fun to return a 2 byte val. I create a 4 byte var to do some processing and return the variable. My assumption is the variable is implicitly converted to short. However, it appears that the returned value isnt rly 2 byte, because the printf fun prints a value greater than 2^16 unless I place (short) before fun(). Why is that?

short fun()
{
long var = 2<<20;
return var;
}

printf("%i", fun());

Upvotes: 0

Views: 97

Answers (3)

Eric Postpischil
Eric Postpischil

Reputation: 222272

In return var;, the value of var is converted to the return type of the function, which is short. Since the value is outside the range of short in your C implementation, there is an overflow. The C specification does not define what happens when there is integer overflow.

In spite of this, the behavior is a little odd. In the printf call, many C implementations would use only 16 bits from fun(), promote them to an int, and pass them to printf. Thus, the output would be “0” in spite of the overflow.

What may be happening in your case is that the compiler implements the conversion to short by simply ignoring it and leaving the value in the processor register where it was held for var (and uses the same register to return the function value). This is permitted by the C standard because, if the long value in the register were in the short domain, leaving the bits unchanged produces the same value when interpreted as a short. If the long value is outside the short domain, it does not matter what happens, because the C standard does not define what must happen.

Then the contents of the register are simply passed directly to printf as an argument. Again, this is valid because it produces the right result if the value is in the short domain and anything is permitted if the var value were not in the short domain.

Upvotes: 1

technosaurus
technosaurus

Reputation: 7802

you start with 10 and left shift 20 to 1000000000000000000000 which exceeds the range of short and passes it to a function that gets a variadic argument (...)

when printf (and other variadic function) look at the value it is basically just a memory location and it uses the format specifier to cast it back to the original type. Here is a macro example of what it is basically doing:

#define va_arg(ap,type)     (*(type *)(((ap)+=(((sizeof(type))+(sizeof(int)-1)) \
                            & (~(sizeof(int)-1))))-(((sizeof(type))+ \
                            (sizeof(int)-1)) & (~(sizeof(int)-1)))))

If you added a format specifier that cast it to short, it could see the casted short. Aside from that just keep (short)-ing your fun().

Upvotes: 1

Adam Rosenfield
Adam Rosenfield

Reputation: 400146

Variadic functions such as printf perform what are called the default argument promotions on their operands. In particular, integers narrower than int (such as short) automatically get widened to int when they are passed into variadic functions.

One way to get around this is to use the h length modifier which says "even though you're dealing with an int value, pretend that it's really a short". Then, printf will narrow the value back to short before formatting the output. For example:

printf("%hi", fun());

Upvotes: 3

Related Questions