Q67785: Bit Operations on Char May Produce Unexpected Results
Article: Q67785
Product(s): See article
Version(s): 6.00 6.00a | 6.00 6.00a
Operating System(s): MS-DOS | OS/2
Keyword(s): ENDUSER | buglist6.00 | mspl13_c
Last Modified: 6-FEB-1991
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 versions 6.00 and
6.00a compiler.
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);
}
When compiled with
cl /qc /J foo.c
the results are:
result1 = F9F7F8F6
result2 = F9F7F8F6
When compiled with
cl /J foo.c
the results are:
result1 = F9F6F8F6
result2 = FFFFF8F6
When compiled with
cl /qc foo.c
or
cl foo.c
the results are:
result1 = FFF6FFF6
result2 = FFFFFFF6
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, 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 "Advanced Programming
Techniques" manual on page 422.
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. Finally, if the results without /J
are desired, declare "p" as a pointer to a signed char.
Microsoft has confirmed this to be a problem in the Microsoft C
Compiler versions 6.00 and 6.00a. We are researching this problem and
will post new information here as it becomes available.
THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.
Copyright Microsoft Corporation 1986-2002.