C Surprises
Intro
The following is a collection of C code, which may have side effects or behavior that is not immediately apparent or obvious. It is intended to serve as a collection of interesting behaviors that may inspire folks to dig deeper into the how and why of the C standard and compilers, as well as also providing a warning of the complexities of having your code do what you intended.
Floats
What will the answer be?
The (1/2)
are both int
literals which evaluate to zero.
The answer is zero.
Will it overrun?
Will the following code memset beyond the allocated buffer? Assume the compiler will not optimize anything out, and 2’s compliment.
It depends on the data model in use.
We’re taking a signed int
, casting it to unsigned int
and then size_t
for the malloc, and casting it to size_t
for the memcpy.
On ILP32 (32-bit Windows, Linux, OSX) where the width of size_t is equal to the width of unsigned int, no error occurs.
On LP64 (64 bit OSX and Linux) where unsigned int is 32 bit but size_t is 64 bit, the following can occur:
- The value -1 is passed in by a user, represented as a negative value in the
int
with 2’s complement as 0xFFFFFFFF - In the malloc, the value is cast to an
unsigned int
, keeping it’s value of 0xFFFFFFFF but becomingunsigned
, followed by being cast fromunsigned int
tosize_t
, which is anunsigned int
, so the value becomes 0x00000000FFFFFFFF. - In the memcpy, the value needs to go from 32 bit int to 64 bit (unsigned)
size_t
. The first thing that occurs is the value is cast from signed 32 bit to signed 64 bit. To represent -1 in 64-bit, a sign extension occurs, resulting in the value 0xFFFFFFFFFFFFFFFF. This value is then cast to unsigned, which does not affect the underlying bit pattern - The memcpy size far exceeds the allocated memory, resulting in an attempted out of bounds write
Macros
What is the result of running ./prog
? what about ./prog a b c
? why?
The #define
line is essentially a match/replace. This can be observed by running
gcc -E code.c
.
Running ./prog
results in h()
running, due to the lack of curly braces around
the if body.
Running ./prog a b c
results in h()
and f()
running.