Help me understand this compiler warning.

C

Charles Sullivan

I have a program written in C under Linux (gcc) which a user has
ported to run under AT&T SysV R4.

He sent me a copy of his makelog which displays a large number
of compiler warnings similar to this:

warning: semantics of ">>" change in ANSI C; use explicit cast

The statement to which this applies is:
xuc = ((uc[7] & 0xF0 ) >> 4);

where the declarations for xuc and uc are:
extern unsigned char xuc;
unsigned char uc[20];

I assume this warning means his compiler expects the number
4 in that statement to be cast as an unsigned char, but I
haven't yet found a warning level switch in my gcc compiler
which displays this warning, so can't be sure. There's no
such warning for another statement in the same function:
xi = uc[8] + (uc[9] << 8);

where in this case:
extern int xi;

Is his compiler being overly picky or is the (apparently
expected) cast something that should be done in well-written
C code?

(Note: the variable names shown are simplified for clarity;
the actual names used are much more self-explanatory).

Thanks for your help.

Regards,
Charles Sullivan
 
M

Morris Dovey

Charles Sullivan (in (e-mail address removed))
said:

| I have a program written in C under Linux (gcc) which a user has
| ported to run under AT&T SysV R4.
|
| He sent me a copy of his makelog which displays a large number
| of compiler warnings similar to this:
|
| warning: semantics of ">>" change in ANSI C; use explicit cast
|
| The statement to which this applies is:
| xuc = ((uc[7] & 0xF0 ) >> 4);
|
| where the declarations for xuc and uc are:
| extern unsigned char xuc;
| unsigned char uc[20];

Try:

xuc = ((uc[7] & 0xF0u) >> 4);

I'd use:

xuc = (uc[7] >> 4) & 0x0F;

to ensure that the shifted portion is not promoted to an int.
 
P

pete

Morris said:
Charles Sullivan (in (e-mail address removed))
said:

| I have a program written in C under Linux (gcc) which a user has
| ported to run under AT&T SysV R4.
|
| He sent me a copy of his makelog which displays a large number
| of compiler warnings similar to this:
|
| warning: semantics of ">>" change in ANSI C; use explicit cast
|
| The statement to which this applies is:
| xuc = ((uc[7] & 0xF0 ) >> 4);

I think the compiler wants to see it this way:

xuc = (unsigned char)((uc[7] & 0xF0 ) >> 4);
|
| where the declarations for xuc and uc are:
| extern unsigned char xuc;
| unsigned char uc[20];

Try:

xuc = ((uc[7] & 0xF0u) >> 4);

I'd use:

xuc = (uc[7] >> 4) & 0x0F;

to ensure that the shifted portion is not promoted to an int.

I don't think that does what you want.

N869
6.5.7 Bitwise shift operators

[#3] The integer promotions are performed on each of the
operands.
 
E

ena8t8si

Charles said:
I have a program written in C under Linux (gcc) which a user has
ported to run under AT&T SysV R4.

He sent me a copy of his makelog which displays a large number
of compiler warnings similar to this:

warning: semantics of ">>" change in ANSI C; use explicit cast

The statement to which this applies is:
xuc = ((uc[7] & 0xF0 ) >> 4);

where the declarations for xuc and uc are:
extern unsigned char xuc;
unsigned char uc[20];

I assume this warning means his compiler expects the number
4 in that statement to be cast as an unsigned char, but I
haven't yet found a warning level switch in my gcc compiler
which displays this warning, so can't be sure. There's no
such warning for another statement in the same function:
xi = uc[8] + (uc[9] << 8);

where in this case:
extern int xi;

Is his compiler being overly picky or is the (apparently
expected) cast something that should be done in well-written
C code?

This question is a very good question but not
an easy one to answer.

The warning is intended to alert a programmer to
a potential problem but which isn't a problem
in this case. The code as written will work
fine.

I myself wouldn't want to write a cast here. I
would if I had to to shut the compiler up, but
before that I would try

xuc = (uc[7] & 0xF0u) >> 4;

or

xuc = (uc[7] >> 4) & 0xF;

(probably in that order) and see if one of those
helped. If one did, I'd use that in preference
to casting.
 
M

Morris Dovey

pete (in (e-mail address removed)) said:

| Morris Dovey wrote:
||
|| Charles Sullivan (in (e-mail address removed))
|| said:
||
||| I have a program written in C under Linux (gcc) which a user has
||| ported to run under AT&T SysV R4.
|||
||| He sent me a copy of his makelog which displays a large number
||| of compiler warnings similar to this:
|||
||| warning: semantics of ">>" change in ANSI C; use explicit cast
|||
||| The statement to which this applies is:
||| xuc = ((uc[7] & 0xF0 ) >> 4);
|
| I think the compiler wants to see it this way:
|
| xuc = (unsigned char)((uc[7] & 0xF0 ) >> 4);

Since xuc is unsigned. Casting the result isn't necessary in order for
the assignment to be made as intended. ANDing an unsigned char with an
int /could/ produce a signed int - which, according to 6.5.7(5), could
produce an implementation-defined shifted result.

Better to ensure that the quantity to be shifted is unsigned.

||| where the declarations for xuc and uc are:
||| extern unsigned char xuc;
||| unsigned char uc[20];
||
|| Try:
||
|| xuc = ((uc[7] & 0xF0u) >> 4);
||
|| I'd use:
||
|| xuc = (uc[7] >> 4) & 0x0F;
||
|| to ensure that the shifted portion is not promoted to an int.
|
| I don't think that does what you want.
|
| N869
| 6.5.7 Bitwise shift operators
|
| [#3] The integer promotions are performed on each of the
| operands.

You appear to have missed that part of 6.7.5(3) that says the result
will have the type of the promoted left operand - unsigned if done as
I suggest.

6.3.1.8(1) - rules for integer promotions - adds a bit of
clarification (sort of).
 
J

Jack Klein

I have a program written in C under Linux (gcc) which a user has
ported to run under AT&T SysV R4.

He sent me a copy of his makelog which displays a large number
of compiler warnings similar to this:

warning: semantics of ">>" change in ANSI C; use explicit cast

Prior to the adoption of the 1989/1990 ANSI/ISO standard, some
compilers applied different rules for promoting unsigned types.
The statement to which this applies is:
xuc = ((uc[7] & 0xF0 ) >> 4);

where the declarations for xuc and uc are:
extern unsigned char xuc;
unsigned char uc[20];

Assuming 8-bit characters, which is true of every Linux I have ever
heard of, then uc[7] has a value between 0 and 255 inclusive. Under
any ANSI/ISO version of C, this will cause uc[7] to be promoted to
signed int, since a signed int can hold all the values of an unsigned
char. This is called a "value preserving" promotion.

On the other hand, some pre-standard compilers performed what might be
called "unsigned preserving" promotions. Unsigned char promoted to
unsigned int and unsigned int promoted to unsigned long.

If the pre-standard C compiler for the AT&T implementation used the
old rules, the uc[7] would promote to an unsigned int with a value
between 0 and 255. This would cause a conversion of integer literal
0xF0 to unsigned int, and the bitwise and would be performed on the
two unsigned ints, producing an unsigned int result, which was finally
shifted right.

Under the new rules, uc[7] is promoted to a signed int with a value
between 0 and 255. Since the integer literal 0xF0 already has type
signed int, it is not converted and the bitwise and takes place on two
signed int values, generating a signed int result. This signed int
result is then shifted right.

There are potential surprises in right shifting signed ints (or signed
longs, for that matter) with negative values. It is
implementation-defined whether the sign bit is maintained or
discarded.

In this particular case, however, you will be right shifting a signed
int with a positive value, and this is absolutely well defined as long
as the shift count is within range, which is certainly is. Signed
integer types with a positive value are defined by the standard to
have identical bit representations as the corresponding unsigned types
with the same value, and right shifting yields the same result.

So due to the values used in this case, there is no actual possibility
of a different result on any implementation. With other values,
specifically those that involve right shifting a signed integer type
with a negative value, there could be a difference in the result. The
compiler is warning about the possibility in the abstract, not
checking to see whether it could happen with the specific values in
this particular case.
I assume this warning means his compiler expects the number
4 in that statement to be cast as an unsigned char, but I

That's a totally incorrect assumption. The type of the right operand,
the shift count, does not play any value at all in any promotion of
the left operand or the type of the result. If the type of the shift
count is less than int, it is promoted to either signed or unsigned
int, but the type on which the operation is performed, and the type of
the result, is based strictly on the promoted type of the left
operand.
haven't yet found a warning level switch in my gcc compiler
which displays this warning, so can't be sure. There's no
such warning for another statement in the same function:
xi = uc[8] + (uc[9] << 8);

where in this case:
extern int xi;

Is his compiler being overly picky or is the (apparently
expected) cast something that should be done in well-written
C code?

(Note: the variable names shown are simplified for clarity;
the actual names used are much more self-explanatory).

Thanks for your help.

The code is actually correct, well-defined, and fully portable. But
if you want to eliminate the warning, as Morris suggested, change the
type of the integer literal:

xuc = ((uc[7] & 0xF0u) >> 4);

This should eliminate it.
 
P

pete

Morris said:
pete (in (e-mail address removed)) said:

| Morris Dovey wrote:
||
|| Charles Sullivan (in (e-mail address removed))
|| said:
||
||| I have a program written in C under Linux (gcc) which a user has
||| ported to run under AT&T SysV R4.
|||
||| He sent me a copy of his makelog which displays a large number
||| of compiler warnings similar to this:
|||
||| warning: semantics of ">>" change in ANSI C; use explicit cast
|||
||| The statement to which this applies is:
||| xuc = ((uc[7] & 0xF0 ) >> 4);
|
| I think the compiler wants to see it this way:
|
| xuc = (unsigned char)((uc[7] & 0xF0 ) >> 4);

Since xuc is unsigned. Casting the result isn't necessary in order for
the assignment to be made as intended. ANDing an unsigned char with an
int /could/ produce a signed int - which, according to 6.5.7(5), could
produce an implementation-defined shifted result.

Better to ensure that the quantity to be shifted is unsigned.

||| where the declarations for xuc and uc are:
||| extern unsigned char xuc;
||| unsigned char uc[20];
||
|| Try:
||
|| xuc = ((uc[7] & 0xF0u) >> 4);
||
|| I'd use:
||
|| xuc = (uc[7] >> 4) & 0x0F;
||
|| to ensure that the shifted portion is not promoted to an int.
|
| I don't think that does what you want.
|
| N869
| 6.5.7 Bitwise shift operators
|
| [#3] The integer promotions are performed on each of the
| operands.

You appear to have missed that part of 6.7.5(3) that says the result
will have the type of the promoted left operand - unsigned if done as
I suggest.

6.3.1.8(1) - rules for integer promotions - adds a bit of
clarification (sort of).

The left operand is uc[7], which is of type unsigned char.

Your posted code doesn't do anything to prevent
type unsigned char from being promoted to type int.

Why do you think that in
(uc[7] >> 4)
that the left operand won't be promoted to int?

"If an int can represent all values of the original type, the
value is converted to an int; otherwise, it is converted to
an unsigned int. These are called the integer
promotions."
 
M

Morris Dovey

pete (in (e-mail address removed)) said:

| The left operand is uc[7], which is of type unsigned char.
|
| Your posted code doesn't do anything to prevent
| type unsigned char from being promoted to type int.
|
| Why do you think that in
| (uc[7] >> 4)
| that the left operand won't be promoted to int?
|
| "If an int can represent all values of the original type, the
| value is converted to an int; otherwise, it is converted to
| an unsigned int. These are called the integer
| promotions."

You're absolutely right. I'm not sure how I managed it; but I looked
it up, read it carefully, and concluded that the unsigned char would
be promoted to an unsigned int - very wrong.

Thanks for getting me on track - and to the OP, my apologies for an
incorrect response.
 
C

Charles Sullivan

Many thanks to all who responded - Morris Dovey, pete, ena8t8si,
and Jack Klein. I especially appreciate Jack's detailed explanation
of the historical background.

Regards,
Charles Sullivan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top