help-flex
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: bug(s) in 2.5.19 skel.c


From: Hans-Bernhard Broeker
Subject: Re: bug(s) in 2.5.19 skel.c
Date: Tue, 17 Sep 2002 16:03:21 +0200 (MET DST)

On Tue, 17 Sep 2002, Bruce Lilly wrote:

> Hans-Bernhard Broeker wrote:
> > On Mon, 16 Sep 2002, Bruce Lilly wrote:
> > 
> > 
> >>There is not even a theoretical problem with calloc or
> >>memset there, as ANSI/ISO C section 6.2.2.3 clearly states
> >>that a null pointer has value 0.
> > 
> > 
> > Wrong.  There definitely is a problem, and particularly with pointers.  
> > Calling memset() or calloc() creates "all bytes zero".  Which may seem to
> > be equivalent to "value zero" for the NULL pointer --- but it isn't,
> > because there is no fixed predefined mapping between pointers and
> > integers, including the integer zero.
> 
> No, 6.2.2.3 specifically states that there is a mapping from an
> integral value of zero to a null pointer.  See that text or K&R 2nd
> edition (ANSI C) p. 102.

My wording was a bit imprecise there.  You're right that a well defined
mapping between an integer zero and a null pointer constant exists. But we
weren't talking about assigning or mapping an integer to a pointer value,
in which this mapping would have applied. 

We were talking about writing zero bytes into the memory area of a pointer
variable, which is quite a different action.  Writing to the bytes through
a wrong-type pointer is neither an assignment, an initialization, nor a
cast, as defined by the language.

> >>Regarding integral types, the requirements of ANSI/ISO C section
> >>6.1.2.5 means that for all practical purposes a zero valued integer of
> >>any size will be all zero bits,
> > 
> > 
> > There's no such section in the C9x draft I have here.  Could you give the
> > title of that section so I can look it up?
> 
> If you're looking at a draft, you're not looking at the standard.

I'm aware of that.  Unfortunately, getting ahold of the real standard is
hideously expensive around here.  

> The title of 6.1.2.5 in the 1990 standard (a.k.a. C90) is "Types".
> One requirement for integral types is that the representation of
> nonnegative values for signed and unsigned types of the same size
> be identical. That includes zero of course.  And the only reasonable
> choices that meet that requirement have zero represented as all
> zero bits.

As I said, the C89/C90 version left some areas unexplained.  In the case
at hand, the gray area is whether all bits in the memory used by a
variable are even part of the "representation" this paragraph speaks of.
And it's exacatly those padding bits that can cause trap representations,
and break the usage of memset() to initialize.

> memset specifically deals with a collection of bytes, setting each
> member of that collection to the value of its second argument as
> an unsigned char.

Exactly.  And it specifically does *not* deal with any other type.
Since an integer is not just a collection of bytes, problems can arise if
you treat it like one.

It's not guaranteed at all that all-zeroes is a legal content of those
padding-bits, if any are present.  Thus code will cause undefined
behaviour if uses memset() this way.  It's undisputed that the actual
behaviour will be what you expect on the vast majority of "typical"
platforms, sure, but this doesn't mean you should just ignore the minority
of others, where it won't.

> Note that K&R 2nd ed. p. 167 gives the following code snippet:
> 
> int *ip;
> ip = (int *) calloc(n, sizeof(int));
> 
> which is exactly what's being discussed here, viz. initialization of a
> pointer via calloc.

This snippet is one of the few examples of K&R2 containing factual errors.
The cast to (int *) contained in this snippet, which the lead-in text
describes as a necessity, is *not* necessary at all, as long as the
#include <stdlib.h> is in place.

I would prefer not to rely on such a known-flawed example to prove
anything.

[...]
> can be initialized by
> 
> struct foo *x = (struct foo *)calloc(1, sizeof(struct foo));

The same can be achieved by:

        const struct foo default_foo = {0 };
        struct foo *x = malloc(sizeof(*x));
        *x = default_foo;

Here the compiler does the conversion from a zero to a pointer, and thus
will do the right thing.  And the initializer for the default struct will
automatically extend to initialized whatever elements the struct happens
to have in a given compilation of the code, too.

> and that will work (after recompiling) without change if
> elements are added to or removed from struct foo's definition.

So will my alternative approach.
 
> The alternatives which you suggested would almost certainly lead to
> something remaining uninitialized or to memory corruption as the
> structure definition is changed over the course of time.

They don't, if done correctly. See above.

-- 
Hans-Bernhard Broeker (address@hidden)
Even if all the snow were burnt, ashes would remain.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]