PRB: Bit Operations on Char May Produce Unexpected Results

ID Number: Q67785

6.00 6.00a 6.00ax | 6.00 6.00a

MS-DOS | OS/2

buglist6.00 buglist6.00a buglist6.00ax fixlist7.00

Summary:

SYMPTOMS

In Microsoft C versions 6.0, 6.0a, and 6.0ax, bit manipulations on

type char may produce different results depending on the compiler

switches. The /J and /qc switches have different effects on the

code below when using the Microsoft C Compiler version 6.0, 6.0a,

or 6.0ax.

CAUSE

The differences occur because of a problem in the Quick Compiler.

When an integral promotion is required, the Quick Compiler

incorrectly converts an unsigned char to an unsigned int. According

to ANSI specifications, if all the values of a char can be

represented in an int, it is converted to an int; otherwise, it is

converted to an unsigned int (see Section 3.2.1.1). This is also

documented in the "Microsoft C Advanced Programming Techniques"

manual on page 422.

RESOLUTION

There are a number of ways to work around this problem depending on

the desired results:

- If the intent was to generate results equal to F9F7F8F6 (as the

Quick Compiler with /J did), declare "p" as a pointer to an

unsigned char and modify the equations for "result1" and

"result2" to use unsigned int casts where appropriate. For

example:

result1 = *p | ((unsigned int)*(p+2) << 8);

result1 += ( *(p+1) | ( *(p+3) << 8)) * 0x10000;

-or-

result2 = ((unsigned long)(((unsigned int)*(p+2) << 8) | *p)|

(unsigned long)(( *(p+3) << 8) | *(p+1)) << 16;

- If the results from the full optimizing compiler with /J are

desired, again declare "p" as a pointer to unsigned char and

replace the above mentioned casts with signed int.

- If the results without /J are desired, declare "p" as a pointer

to a signed char.

STATUS

Microsoft has confirmed this to be a problem in C versions 6.0,

6.0a, and 6.0ax. This problem was corrected in C/C++ version 7.0.

More Information:

Sample Code

-----------

#include <stdio.h>

char unsigned ary[4] = {0xF6, 0xF7, 0xF8, 0xF9};

void main(void)

{

unsigned long result1, result2;

char *p;

p = ary;

result1 = *p | ( *(p+2) << 8);

result1 += ( *(p+1) | ( *(p+3) << 8)) * 0x10000;

result2 = (unsigned long)(( *(p+2) << 8) | *p) |

(unsigned long)(( *(p+3) << 8) | *(p+1)) << 16;

printf("result1 = %lX\n", result1);

printf("result2 = %lX\n", result2);

}

More Information:

When compiled with /qc and /J, the results are:

result1 = F9F7F8F6

result2 = F9F7F8F6

When compiled with /J, the results are:

result1 = F9F6F8F6

result2 = FFFFF8F6

When compiled with /qc or no options, the results are:

result1 = FFF6FFF6

result2 = FFFFFFF6

Additional reference words: 6.00 6.00a 6.00ax