Cast alignment warnings

Russ Allbery rra at stanford.edu
Tue Aug 2 20:42:41 UTC 2011


Julien ÉLIE <julien at trigofacile.com> writes:

> Many thanks for the rewording.  It is very clear.
> You have a rare talent to explain things in simple, clear words.  Not
> everybody is capable of that.  -- Well, I just wanted to tell you that
> fact, but I do not doubt you were already aware of that :)

Thank you!

>> Integers in C can be 16, 32, or 64 bits depending on the architecture

> How can we then print size_t values?
> As we do not know if it is an unsigned int or an unsigned long int, how
> are we supposed to use printf or other similar functions?
> Is "%z" possible/portable?

Yeah, that's a problem, and indeed is why %z was introduced.  But %z is
not particularly portable yet.

However, in practice, every C implementation that I'm aware of guarantees
that size_t is no larger than unsigned long, so generally the portable way
to handle this is to cast to unsigned long and use %ul.

> And if I write:

>   size_t i = 1;
>   printf("%lu", (unsigned long int) i);

> Wouldn't it trigger an issue when i is an unsigned int?  "%lu" will read
> too many bytes, won't it?  And also cause an issue on big-endian
> systems, won't it?

Casts of integer values are different than casts of pointers.  If you cast
a pointer, nothing changes about the value of the pointer; the compiler
just assumes it points to something different.  But if you cast a number,
the number is read from the variable first, and then cast, and the
compiler will truncate or broaden the number (in an implementation-defined
way) if necessary to make it fit the new type.

Think of it this way: If you have a pointer to an integer, and you cast it
to a pointer to an unsigned long, in both cases the "value" of the thing
is a memory address.  So nothing has to change about the value, since both
the source type and the target type are memory addresses.  The compiler
may then use that memory address in a different way, but that happens
later on.

But if you cast the actual integer, you're casting, say, the value 56 to a
different type.  So the compiler will actually convert.  (Including
complete conversions of form, such as if you were casting to a float or a
double instead of a different integer type.)

For a normal function call, this conversion happens regardless due to the
function prototype, and no cast is necessary.  If you have a function:

    void foo(unsigned long);

and you call it like:

    int i = 4;
    foo(i);

the compiler writes code to retrieve the value of i, convert it to an
unsigned long due to the prototype, and write it into an unsigned long's
worth of space in the stack.

printf is a variadic function, though, which means that the compiler
doesn't have type information.  So if you do:

    int i = 4;
    printf("%lu", i);

the compiler will trust the types that you give it and will put a pointer
to a string and then a int-sized value on the stack.  If the platform has
32-bit ints and 64-bit longs, printf will then read 64-bits off the stack
when only 32-bits were written and will get random garbage off the end of
the number.

But if you write:

    printf("%lu", (unsigned long) i);

you're basically giving the compiler back the information that it would
have when foo() was being called above, so it knows that argument is an
unsigned long, and will store 64 bits in the stack.

The short version is that as long as you cast the arguments to printf to
the right type for the format string, you won't have any reads of random
invalid bits of memory; the worst that can happen is that you may truncate
the value if the type you cast it to is too small.  And unsigned long is
basically guaranteed to always be large enough for a size_t.  (It's *not*
necessarily guaranteed to be large enough for a pointer, and I believe on
at least some versions of Windows it's not.)

sscanf is a different case because all its arguments are pointers, not
values, so casting doesn't really help you and the pointers have to
actually point to the right amounts of storage.

-- 
Russ Allbery (rra at stanford.edu)             <http://www.eyrie.org/~eagle/>

    Please send questions to the list rather than mailing me directly.
     <http://www.eyrie.org/~eagle/faqs/questions.html> explains why.



More information about the inn-workers mailing list