Does this, or a similar routine, exist in the ANSI standard version of C?
If so, I take it that you can only use it e.g. on arrays, but not
structures, since a structure might contain a pointer, and a NULL pointer may
not be represented as 0?
In other words, is it true that in ANSI C I have to write a special-purpose
allocator for each structure, the general form of which is
someStruct *allocSomeStruct()
{
malloc()
bzero()
set each pointer to NULL
return
}
to ensure pointers in structures are `really' null?
--Tom Kronmiller
t...@cadence.com or ...!{hpda,versatc,apollo,ucbcad,uunet}!cadence!twk
Yes, memset() is the standard function that can be used for this purpose
(also others, since you tell it what byte value to fill with).
>If so, I take it that you can only use it e.g. on arrays, but not
>structures, since a structure might contain a pointer, and a NULL pointer may
>not be represented as 0?
It's worse than that. If there is any object having a base type other
than an integral type in the region being set to zero bytes, not enough
is promised about its representation for such an approach to be used
portably (unless you don't care what values result in such objects).
>In other words, is it true that in ANSI C I have to write a special-purpose
>allocator for each structure, ...
It's not ANSI C that does this to you, but rather the variety of
computing architectures. As often happens, the ANSI C standard merely
makes explicit a portability constraint that has always existed but of
which many programmers were previously unaware.
A simpler way to zero a struct foo is:
static struct foo zero_foo; /* init all 0 values */
...
struct foo *foop = (struct foo*)malloc( sizeof(struct foo) );
...
*foop = zero_foo; /* struct assignment */
You can use it on structures containing only integral types, and you cannot use
it on arrays containing pointers. It's not whether it's an array or structure,
but whether it contains any non-integral type objects.
>In other words, is it true that in ANSI C I have to write a special-purpose
>allocator for each structure, the general form of which is
> someStruct *allocSomeStruct()
> {
> malloc()
> bzero()
> set each pointer to NULL
> return
> }
>to ensure pointers in structures are `really' null?
Yes, but this was always true. With ANSI C it continues to be the case that
careless uses of bzero() will work ok anyway on the vast majority of systems,
and prior to ANSI C it was already the case that careless uses of bzero() would
not work on some systems.
ajr
--
"Learning algorithms from Knuth is almost as bad as learning physics from
Newton in the Latin original." -- Jeffrey Kegler
ANSI C includes memset(void *region, int value, size_t size) which copies
"value" into each char of the region. You can use it on anything; however,
if the region will later be interpreted as containing pointers or floating-
point variables, the effect is very machine-specific and quite unportable.
>In other words, is it true that in ANSI C I have to write a special-purpose
>allocator for each structure...
>to ensure pointers in structures are `really' null?
If you are depending on the result from the allocator to be initialized,
then yes, your allocator has to initialize it, and the only portable way
to initialize pointers and floating-point variables is to do explicit
assignments to them, either directly or by assigning to the whole struct.
Usually, however, this is not necessary, because the first thing you do
when you get a new struct from an allocator is to fill it with your own
data. Why should an allocator go to a lot of effort filling in values
that will be overwritten immediately anyway?
--
"Apparently IBM is not IBM | Henry Spencer at U of Toronto Zoology
compatible." -Andy Tanenbaum | uunet!attcan!utzoo!henry he...@zoo.toronto.edu
How about:
struct foo *allocFooStruct()
{
struct foo *newstruct;
static struct foo zero;
foo = (struct foo *)malloc(sizeof(struct foo));
*foo = zero;
return foo;
}
--
_.John G. Myers Internet: j...@fed.expres.cs.cmu.edu
(412) 268-2984 LoseNet: ...!seismo!ihnp4!wiscvm.wisc.edu!give!up
"It's not bogus, It's an IBM standard" --Esther Filderman
...but also checking the return value of malloc(), of course...
I already told you one in my previous response.
In fact, I've run into a variant of this problem in a very popular,
"reasonably portable" program, the MIT sample X server, when running on
Symbolics Lisp Machines, which are very picky. Many structures have fields
that are only used if certain other fields have particular values. Some
routines allocate such structures, and don't bother to zero the "don't
care" fields. The structures are then passed to a routine to transmit the
bytes over the net (actually, I encountered problems in the byte-swapping
routines, but I believe the same problems would occur without byte
swapping). In the Symbolics implementation, uninitialized words contain a
value (Lisp's NIL) that will generate an error if an attempt is made to
read a byte or half-word from it.
I think the Symbolics implementation is valid in this regard, but I'll bet
many programmers assume that even uninitialized variables can be
byte-swapped and/or transmitted over byte streams.
--
Barry Margolin, Thinking Machines Corp.
bar...@think.com
{uunet,harvard}!think!barmar
Answers fell into 3 categories:
1. Use `memset' where bzero() was legal; beware of pointers/floats.
2. If you're not already writing those initializing routines,
you should be.
3. No need to zero the fields; whoever uses the records will
stuff new values into them ASAP, anyway.
To those of you whose reply was (1), thanks.
For the rest of you, I assert that while (3) is often true, it's not
invariably true, especially in large projects where several people may
allocate the same record, but each uses only a subset of the fields.
For this reason (among others) I have already been doing (2) for a long
time.
But, I observe as a practical matter that when some person comes to me and
says "I need a new pointer in the foobar structure", it is much more likely
that I forget to add the "ptr=NULL" to the initializer than it is that I
forget to add the "type *ptr" to the structure definition. The latter is
very easy to detect -- the requestor comes to me, screaming that I screwed
up. The former is harder -- only if garbage happens to land in the field
do we detect it.
Thus, I would like a `maintainence safe' way to zero a record when I
allocate it. Perhaps, since the compiler knows the types, there could
be a compiler-provided procedure
zeroArbitraryRecord( pointerToTheRecord )
which sets all fields to "(type)0", enabling me to write initializers
which don't need to change (as much as) as the structure definition changes.
I assume the standard does not provide such support, since no one mentioned
it. I was hoping that in all the fighting over whether or not NULL pointers
had to be represented as all-bits-0, someone might have considered that
it would be useful to have a portable, maintainable, zero-er; but apparently
not.
I don't think anybody claimed otherwise. They just noted that since it
is true much of the time, it's probably a bad idea to always initialize
newly-mallocated data. I'd vote for the "static struct mumble zero"
technique suggested by others as the best way to zero out a structure.
A compiler could even play tricks to avoid allocating the structure if
it's all zero bits....
I tend to define portable as:
Works under existing set of machines/compilers that I use or
expect to use, taking their limitations/bugs into account.
Rather than:
What the ANSI C standard says is 'maximally portable'.
In other words, go ahead and use memset to zero out a struct. If you allocate
it, use calloc. I use lots of different machines, and have never
encountered one with 0.0 != 0 bits or (void*)0 != 0 bits.
OK, netters, here's the request. Lets form a list of all the machines
that have either of these properties. Anyone know of any machines being
designed with these properties? This list can be used to help decide
if the problem is worth worrying about.
It obviously depends on the compilers you expect to be using.
In any event, if you do not take into account what the ANSI standard
says even if you don't exploit ANSI-specific features, you'll restrict
the future portability of your applications. For example, <varargs.h>
may not exist in many future implementations, so you should conditionalize
your variadic function definitions to work both with <varargs.h> and the
new <stdarg.h>, depending on __STDC__. There is no particular need to
immediate switch to using function prototypes, assuming you write your
function definitions in terms of default-widened types. You need to be
careful to use proper type casts in some circumstances, but if done right
the same code should work for both old C environments and ANSI C ones.
You should avoid relying on behavior that the standard does not guarantee.
By exerting this kind of care, the vast majority of C application code can
work portably, including using both pre-ANSI and ANSI C implementations.