The C standard specifies the behavior of code in terms of an abstract machine. This specification does not know about concepts such as "the stack". It knows variables with "automatic storage duration", meaning variables which are "gone" when the scope they've been defined in is left.
From the perspective of the C standard the code
local = 0; has only the effect that subsequent accesses to
local will yield the value
Now, even though it's not explicitly called like this in the standard, C has an "as-if rule". This means that a compiler is free to implement the code however it likes as long as the "observable behavior" remains the same as that of the code when "run" by the abstract machine.
local = 0; has no observable behavior (assuming there's no code using
local in an observable way after the assignment). Therefore, the compiler is allowed to discard that assignment; regardless of optimization settings.
Thus, "zero-ing" like this is a sin because it is not guaranteed to actually do anything and adds clutter to the code, making it less readable. Note that this also applies to dynamically allocated memory!
edit It's questionable if it's even possible to make sure the compiler doesn't optimize the zeroing away, see this blog post, linked post and comments
To really zero a variable You need to make this assignment "observable". The C standard luckily includes means to do so: via an assignment to an object of
volatile qualified type.
So with a function like ...
void zero_out(unsigned char volatile * memory, size_t size)
*memory++ = 0;
... You can be sure that after calling
zero_out(&local, sizeof(local)) the memory region (if any) where
local is/was kept is zero. This disables any smart optimizations by the compiler, though, so for larger memory regions it might be slow!
Even better than my above function is to use a function provided by either the standard or the compiler to achieve the same (because it might be optimized):
- C11 has
memset_s which won't be optimized away. Problematic because
memset_s is only optional, and currently not available on e.g. GCC and Clang.
- MSVC has
There are also other (potentially better) ways to implement my
zero_out from above:
volatile function pointer:
void * (*volatile memset_here_to_stay)(void *, int, size_t) = memset;
void zero_out(void * p, size s)
memset_here_to_stay(p, 0, s);
An "external" function, implemented in assembly (or generated by another translation unit). Attention! Link time optimization might make this ineffective ...