Help me understand this compiler warning.

Discussion in 'C Programming' started by Charles Sullivan, Aug 12, 2006.

  1. 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
     
    Charles Sullivan, Aug 12, 2006
    #1
    1. Advertising

  2. Charles Sullivan

    Morris Dovey Guest

    Charles Sullivan (in )
    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.

    --
    Morris Dovey
    DeSoto Solar
    DeSoto, Iowa USA
    http://www.iedu.com/DeSoto
     
    Morris Dovey, Aug 12, 2006
    #2
    1. Advertising

  3. Charles Sullivan

    pete Guest

    Morris Dovey wrote:
    >
    > Charles Sullivan (in )
    > 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.

    --
    pete
     
    pete, Aug 12, 2006
    #3
  4. Charles Sullivan

    Guest

    Charles Sullivan wrote:
    > 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.
     
    , Aug 12, 2006
    #4
  5. Charles Sullivan

    Morris Dovey Guest

    pete (in ) said:

    | Morris Dovey wrote:
    ||
    || Charles Sullivan (in )
    || 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).

    --
    Morris Dovey
    DeSoto Solar
    DeSoto, Iowa USA
    http://www.iedu.com/DeSoto
     
    Morris Dovey, Aug 13, 2006
    #5
  6. Charles Sullivan

    Jack Klein Guest

    On Sat, 12 Aug 2006 21:25:56 GMT, Charles Sullivan
    <> wrote in comp.lang.c:

    > 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.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://c-faq.com/
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
     
    Jack Klein, Aug 13, 2006
    #6
  7. Charles Sullivan

    pete Guest

    Morris Dovey wrote:
    >
    > pete (in ) said:
    >
    > | Morris Dovey wrote:
    > ||
    > || Charles Sullivan (in )
    > || 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."

    --
    pete
     
    pete, Aug 13, 2006
    #7
  8. Charles Sullivan

    Morris Dovey Guest

    pete (in ) 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.

    --
    Morris Dovey
    DeSoto Solar
    DeSoto, Iowa USA
    http://www.iedu.com/DeSoto
     
    Morris Dovey, Aug 13, 2006
    #8
  9. Re: Help me understand this compiler warning. THANKS GUYS!!!

    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
     
    Charles Sullivan, Aug 16, 2006
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tobias Langner

    compiler error I don't understand

    Tobias Langner, Aug 1, 2003, in forum: C++
    Replies:
    7
    Views:
    331
    Jakob Bieling
    Aug 1, 2003
  2. forgotten field

    can't understand the compiler messages

    forgotten field, Apr 4, 2004, in forum: C++
    Replies:
    5
    Views:
    472
    jeffc
    Apr 6, 2004
  3. Pete Becker
    Replies:
    0
    Views:
    1,382
    Pete Becker
    Feb 10, 2005
  4. jt
    Replies:
    4
    Views:
    582
    Fred L. Kleinschmidt
    May 16, 2005
  5. B. Williams

    warning C4267 and warning C4996

    B. Williams, Oct 26, 2006, in forum: C++
    Replies:
    17
    Views:
    2,640
Loading...

Share This Page