What do you this should be the output of the below code? Is there any problem in the code?

#include <stdio.h>

int main(void)
{
  int x = -1, y = 1, a, b, c, d, e, f;

  a = x << 4;
  b = x >> 4;
  c = y << 4;
  d = y >> 4;
  e = y << sizeof (int) * 8;
  f = y << -4;

  printf("a = %x \nb = %x \nc = %x \nd = %x \ne = %x \nf = %x",a, b, c, d, e, f);

  printf ("\n");
  return 0;
}

Execution result

The execution of this piece of code is shown below:

a = fffffff0 
b = ffffffff 
c = 10 
d = 0 
e = 1 
f = 10000000

Wait, it’s not the end.

{Implementation Defined, Undefined} Behaviour: What you see is not always what is it in reality

The code will compile and an executable would be generated and which will give you a certain output. But there are many problems hidden in this code, which does not adhere to the standards. Basically this code includes every possible combination of operation covering all UD/ID behaviours using shift operators.

In one word the code will exhibit implementation defined and undefined behaviour for two causes. Before explaining the problems, let me quote from the C99 standards (also present in C11 standards in the same section) the paragraph based on which all the problems will be explained.

  • C89/99 Standard Section 6.5.7 Paragraph 3

    The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

  • C89/99 Standard Section 6.5.7 Paragraph 4

    The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 x 2E2 , reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that the resulting value; otherwise, the behavior is undefined.

  • C89/99 Standard Section 6.5.7 Paragraph 5

    The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type
    or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined.

Explanation

  • Line 7: Problem 1

    To consider line 7 alone means the below code basically.

    int x = -1, a;
    a = x << 4;
    

    Here we are left shifting a signed integer which has a negative value. Therefore clearly according to the segment “If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that the resulting value; otherwise, the behaviour is undefined.

    According to this underlined section the code does not comply to standards and is an undefined behaviour.

  • Line 8: Problem 2

    In this case the significant portion of the code is

    int x = -1, b;
    b = x >> 4;
    

    Now we are right shifting a signed integer with a negative value. Here also the following line clearly states that this is implementation-defined behaviour. “If E1 has a signed type and a negative value, the resulting value is implementation-defined.”

    Note the difference, this is implementation defined behaviour and the previous one was undefined behaviour. In the previous case the standards fix no rules what will result, and in this case the result will depend on the current implementation, that is depends on the compiler and platform.

    In this case, if the signed bit is set (1), then it may or may not propagate, depending on the implementation of the language, ie. the compiler.

  • Line 9 and 10: No Problem

    Below snippet shows the relevant portion

    int y = 1, c, d;
    c = y << 4;
    d = y >> 4;
    

    In line 9 we are left shifting a positive signed integer 4 places (less than the bit width of the type) therefore it is perfectly all right. The vacated right sides will be filled with zeros when shifting. Also the end result is representable with the type of the operand on the result type.

    On the other hand in line 10 we are right shifting a positive signed integer 4 places (less than the bit width of the type). Therefore the answer as per defined will be the integer part of the quotient 1/24 which is 0.

  • Line 11 and 12: Problem 3 and 4

    The code related to this problem is

    int y = 1, e, f;
    e = y << sizeof (int) * 8;
    f = y << -4;
    

    Now, line 10 violates the C89/99 Standard Section 6.5.7 Paragraph 3, precisely ” If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.”. This line is trying to shift the integer greater than its bitwidth (taking 1 byte = 8 bits check C Q&A #1: How many bits are there in a byte?)

    On the other hand line 11 falls on the other case. In this case the right hand side operand is negative.

    If you have -Wall enabled in gcc or have a good compiler then these lines will throw you warnings.
    In gcc I get.

    sh.c: In function ‘main’:
    sh.c:11:3: warning: left shift count >= width of type [enabled by default]
    sh.c:12:3: warning: left shift count is negative [enabled by default]
    

    Therefore both these cases exhibit undefined behaviour.

Conclusion

Although you will get warnings, if compiler warnings are enabled, when the right hand side operand has a value greater or equals to the width of the type on the right hand, or, if the right hand side operand is negative, but in the other cases, that is, shifting a signed integer variable having a negative value, will have no warnings. The cause is straight forward, it is not possible to know the contents of a variable at compile time.

Therefore whenever you shift watch your step.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s