Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Another reason to not memset structures: In the code

    struct foo {
        void * bar;
    } baz;
    
    ...
    
    memset(&baz, 0, sizeof(struct foo));
    assert(baz.bar == NULL);
it's possible for the assertion to fail, since NULL it not guaranteed to be represented in memory by a zero bytes.


This is a weak reason. In practice, machines that do not represent NULL as zero no longer exist and new designs of this nature would break so much existing code, it would be difficult for them to gain wide adoption in the marketplace. The C standard hasn't decided to require NULL be represented as zero yet (and may never, to preserve compatibility with machines it used to have defined behavior on), but most C code runs on machines where NULL is represented with zero bits.

(You're absolutely correct that the standard does not require NULL be represented with zero bits, though.)


I feel like a compiler is within its rights to optimize out that check if it so wishes, though.


> it's possible for the assertion to fail, since NULL it not guaranteed to be represented in memory by a zero bytes.

Technically correct (which is of course the best kind of correct) but you'd be hard pressed to find a system in 2019 where NULL != (void*)0. The most recent machines with non-zero NULL in the C FAQ entry on the matter (http://c-faq.com/null/machexamp.html) date back to the mid '90s.


>NULL != (void* )0.

This expression is false in any C99 compliant system:

Section 6.3.2.3 [0]:

> An integer constant expression with the value 0, or such an expression cast to type void * , is called a null pointer constant. [1] If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function

> Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

The technicality of the memset example is that it does not set the bits of the pointer by referring to it as a pointer, so the requirement that 0 behave as if it was a null pointer does not apply.

[0] http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf

[1] In text footnote: The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.17

EDIT: Formatting


The literal `0` is guaranteed to be the null pointer value when used as a pointer, so by definition

    NULL == (void*)0.
But given

    void *p = 0; 
    intptr_t i = 0;
it is not guaranteed that `memcmp(&i, &p, sizeof(p)) == 0`.


> it is not guaranteed that `memcmp(&i, &p, sizeof(p)) == 0`.

Even less intuitively, it is not guaranteed that `(void*)i == p` since `i` is not an integer constant expression, even if the value is known to be 0.


> you'd be hard pressed to find a system in 2019 where NULL != (void *)0

You'd be hard pressed to find a system at any time where NULL is not equal to a compile-time constant zero. ;-)


[flagged]


You're missing the point. "(void *)0" is guaranteed to be a NULL pointer, even when said pointer is not represented in memory by zeroes.


Nah, NULL is guaranteed to be == (void * )0 by the standard. The allowed divergence is in how (void * )0 is represented as bits in memory, i.e., memcmp(zeroes, (ptr = NULL), sizeof(ptr)) == 0?


Technically no such system exists ;)

But talking about the actual bit representation. A zero value as a special pointer with the magic property of being invalid is a convention for C on x86, right?

I assume it has to do with it's fast to check the zero-flag in EFLAGS register when doing checks for null pointers.


That will never happen on current or future platforms, and is not a big concern. Or perhaps it'd be better to say that if it ever does happen on a future platform, then a few memset calls are going to be the least of your problems when porting legacy C code to that platform.

However, what has bitten me is memsetting structures that I later turn into full-fledged classes in C++. Oops, there went the VMT.

Designated initializers are very nice, as is the ability (in C++) to provide initial-value assignments that run before the constructor.


I do wish there was a compact way to tell a C++ class to zero init all pointer/numeric members to zero.


What's wrong with:

  T foo = {}


I mean in the constructor... Can one do *this ={}?


...are there any remaining architectures where NULL is not zero?

The examples listed here are all historical:

http://c-faq.com/null/machexamp.html


It does not matter if there are any architectures like that. Relying on that fact is still an undefined behavior. The compiler is allowed to produce any code it wants.

If you use memset to initialize structure with pointers with 0 and then test if the pointer are NULL, the compiler could assume that the pointer was not properly initialized and remove the if completely, or actually even remove the whole function.


In the real world it rarely matter what a theoretical compiler is allowed to do, but what specific compilers actually do. UB only matters when compilers actually "exploit" it.


Are you accepting bug reports for such issues in tarsnap? :)


Yes. If you look at the commit history you'll find that we've fixed a few of them already.


Yeah, I saw the comment and big block of bulls in bad tar. Here's two from memory: parent pointer in tree_entry, although it seems unused anyway. buff pointer in the link hash thing. (My bad, I looked at it on my laptop, which is not here anymore. I can drop an email later.)


Thanks! I'll try to remember about this when I'm back at my laptop but an email will make sure I don't forget.

FWIW I'm less strict about standards compliance in the libarchive-derived code, since that frobs lots of unportable bits anyway.


Used to read your blog several years ago, and really enjoyed it. Good seeing your name pop up again.


You are right in theory, but I have yet to come upon a compiler that will use something other than zero for null.


So if you're zeroing out a struct like so

  struct S {size_t a, void *p;};

  foo = (struct S) {0};
Does the standard require the compiler to set any pointers to NULL, or will everything be implicitly zeroed leaving pointers possibly improperly initialized to NULL?


The pointers are required to be initialized to NULL in your example (even if the representation is different from memset of zero).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: