zero up memory

M

Mark

Hello,

consider the following code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define NAME_LEN 16
#define MAX_INSTANCES 64

struct inst_info
{
uint32_t state;
uint32_t port_mem;
};

struct vid_entry
{
struct vid_entry *prev;
struct vid_entry *next;
uint16_t vid;
uint32_t vid_state;
};

struct hsl
{
char name[NAME_LEN + 1];
uint32_t ageing_time;
unsigned char flags;
int learning;

/* Platform specific information. */
void *system_info;

struct vid_entry *inst_table[MAX_INSTANCES];
struct inst_info inst_info_table[MAX_INSTANCES];
};

int main(void)
{
struct hsl *br;

br = malloc (sizeof (struct hsl));
if ( !br)
{
puts("malloc() failed");
return -1;
}

memset ((void *)&br->inst_info_table, 0, sizeof (struct inst_info) *
MAX_INSTANCES);
memset ((void *)&br->inst_table, 0, sizeof (struct vid_entry *) *
MAX_INSTANCES);

free (br);

return 0;
}

As I understand it, 'inst_table' is the array of pointers to 'struct
vid_entry' and on my system pointer is 32-bit wide, so I want to make sure
that the code is correctly initializing memory allocated for 'struct hsl'
object.

Also, is (void *) necessary or more a pedantic element?

Thanks !

Mark
 
B

Ben Bacarisse

Mark said:
Hello,

consider the following code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define NAME_LEN 16
#define MAX_INSTANCES 64

struct inst_info
{
uint32_t state;
uint32_t port_mem;
};

struct vid_entry
{
struct vid_entry *prev;
struct vid_entry *next;
uint16_t vid;
uint32_t vid_state;
};

struct hsl
{
char name[NAME_LEN + 1];
uint32_t ageing_time;
unsigned char flags;
int learning;

/* Platform specific information. */
void *system_info;

struct vid_entry *inst_table[MAX_INSTANCES];
struct inst_info inst_info_table[MAX_INSTANCES];
};

int main(void)
{
struct hsl *br;

br = malloc (sizeof (struct hsl));
if ( !br)
{
puts("malloc() failed");
return -1;
}

memset ((void *)&br->inst_info_table, 0, sizeof (struct inst_info) *
MAX_INSTANCES);
memset ((void *)&br->inst_table, 0, sizeof (struct vid_entry *) *
MAX_INSTANCES);

free (br);

return 0;
}

As I understand it, 'inst_table' is the array of pointers to 'struct
vid_entry' and on my system pointer is 32-bit wide, so I want to make sure
that the code is correctly initializing memory allocated for 'struct hsl'
object.

Well, it zeros only two elements of the struct hsl object. The other
elements are still indeterminate.

There is a technical issue that memset sets the memory to all zero bits
whereas C permits a null pointer to be represented by something else.
It's therefore possible that memset is setting the pointers to some
undesirable value. You may spend your whole life without ever seeing
such a system but, once you know about it, it's hard not to prefer a
loop:

for (int i = 0; i < MAX_INSTANCES; i++)
br->inst_table = NULL;
Also, is (void *) necessary or more a pedantic element?

No, it's not. Neither is the & since (in this situation) an array is
converted to a pointer to it's first element. Also, you can simplify
the code by making better use of sizeof:

br = malloc (sizeof *br);

Just by looking, I know sizeof *br is the right size -- I don't have to
check the definition of br. Then later:

memset (br->inst_info_table, 0, sizeof br->inst_info_table);
memset (br->inst_table, 0, sizeof br->inst_table);
 
J

Johann Klammer

Hello,

It is a bit shorter if you do:

br = calloc(1,sizeof (struct hsl));
if ( !br)
....[as above]


calloc implicitly zeroes the allocated memory(all the struct fields).
 
M

Malcolm McLean

calloc implicitly zeroes the allocated memory(all the struct fields).
Yes. Calloc() was specified before the C standard was worked out.

All bits zero isn't necessarily a floating point zero, and it isn't
necessarily a null pointer. Which makes calloc() pretty useless.
 
K

Keith Thompson

Malcolm McLean said:
Yes. Calloc() was specified before the C standard was worked out.

All bits zero isn't necessarily a floating point zero, and it isn't
necessarily a null pointer. Which makes calloc() pretty useless.

It's useful if the allocated memory doesn't contain any floating-point
or pointer subobjects.

(it can also, I hesitate to mention, be useful in code that isn't
intended to be portable.)
 
M

Malcolm McLean

It's (calloc) useful if the allocated memory doesn't contain any floating-point
or pointer subobjects.
It's too easy to change a structure to add a floating point or pointer
field, test it, and of course find that the code behaves exactly as
you want.
Calloc() is best avoided.
 
K

Keith Thompson

Malcolm McLean said:
It's too easy to change a structure to add a floating point or pointer
field, test it, and of course find that the code behaves exactly as
you want.
Calloc() is best avoided.

Agreed, mostly -- but if the allocated space contains an array of some
integer type, it's not unreasonable to use calloc() (with a comment).
But yes, there is a risk if the integer array is later changed to a
floating-point array.

(Hmm, I wonder if there are any systems where all-bits-zero is not a
representation of 0.0 for all floating-point types. Could a future
standard impose such a requirement without breaking existing
implementations?)
 
K

Kaz Kylheku

It's useful if the allocated memory doesn't contain any floating-point
or pointer subobjects.

It's useful even if it isn't, just no portable.

Nonportable and useless are different things.

I can think of a lot of code that will never need to run on a machine
where null pointers or 0.0 aren't all zero bits.

All-zero memory can be very efficiently procured on a virtual memory OS:
you can alias all of the pages to the same physical frame of memory.

This is very useful for, for instance, large sparse matrices.

An architecture where zero bits don't make a 0.0 floating point value
is plain stupid (and not IEEE 754 compatible).
 
J

Joel C. Salomon

An architecture where zero bits don't make a 0.0 floating point value
is plain stupid (and not IEEE 754 compatible).

How about a 754 implementation where the in-core representation is the
bitwise complement; that way the all-bits-zero memory word represent the
all-bits-one datum which (on this hypothetical platform) is the
signaling NaN.

However crazy this may be, "plain stupid" it's not -- and it should be
754 compliant.

--Joel
 
M

Malcolm McLean

An architecture where zero bits don't make a 0.0 floating point value
is plain stupid (and not IEEE 754 compatible).
Zero cannot be represented in floating point, because the exponent is
minus infinity.

If you have a signed exponent, zero would represent unity, or number
within the range 1 - 2. You might reserve high bit set for special
numbers, and zero could be one of those special numbers.
Most existing floating point formats use an offset to represent
exponents, so zero exponent represents the lowest representable value,
and all bits zero can be used for zero. However this isn't
particularly logical, and there might be low-level circuit reasons for
preferring a two's complement exponent.
 
B

Ben Bacarisse

Kaz Kylheku said:
It's useful even if it isn't, just no portable.

Which is what Keith said immediately after the remark you quote. Why
cut it? You could have left it in and still make the point you want to
make (the insanity and scarcity of these odd architectures) without
suggesting that Keith was excluding the use of calloc in non-portable
code.

<snip>
 
B

Ben Bacarisse

Malcolm McLean said:
Zero cannot be represented in floating point, because the exponent is
minus infinity.

In systems where the mantissa is not normalised (i.e. there is no
implicit leading 1) then zero is specified quite naturally with a zero
mantissa. A zero sign bit and exponent (even it biased) makes no
difference. Systems like this are common: IEEE decimal floating point
and IBM's hexadecimal floating point come to mind.
If you have a signed exponent, zero would represent unity, or number
within the range 1 - 2. You might reserve high bit set for special
numbers, and zero could be one of those special numbers.
Most existing floating point formats use an offset to represent
exponents, so zero exponent represents the lowest representable value,
and all bits zero can be used for zero. However this isn't
particularly logical, and there might be low-level circuit reasons for
preferring a two's complement exponent.

In IEEE binary floating point formats, a zero exponent is special. It
is not simply the lowest exponent, but flags up a set of special values:
+0 and -0 when the mantissa is 0 and denormalised numbers when it
is not zero (and the actual exponent used is 1 not zero i.e. the lowest
"real" exponent).
 
M

Malcolm McLean

In systems where the mantissa is not normalised (i.e. there is no
implicit leading 1) then zero is specified quite naturally with a zero
mantissa.  A zero sign bit and exponent (even it biased) makes no
difference.  Systems like this are common: IEEE decimal floating point
and IBM's hexadecimal floating point come to mind.
So how does the processor do equality comparisons, efficiently?

If the mantissa is not normalized then there are many possible
representations for each number, including zero.
 
J

Joe keane

All bits zero isn't necessarily a floating point zero, and it isn't
necessarily a null pointer. Which makes calloc() pretty useless.

To be honest, i don't think it's a big problem. I have code where i
simply 'memset' the whole struct, and i'm sure it's been ported to
twenty machines. No one ever comes back and says 'i ran into that
thing where you memset the struct, it didn't work'.

So there's practical portable versus paranoid portable.

It's something i just don't care about and i think is not worth spending
your time on. My employer doesn't care i don't care because, while
customers may have pretty diverse platforms, they're not geting stuff
from bizarro world.

Architectures can be different, but there really isn't much variation
these days in how basic data types [integer, floating-point number,
pointer] are stored [and how VM basically works].

This is something that people were unsure about 40 years ago; now we
'have the answer'. Such things seem sufficient obvious that you don't
think about it much.
 
B

Ben Bacarisse

Malcolm McLean said:
So how does the processor do equality comparisons, efficiently?

If the mantissa is not normalized then there are many possible
representations for each number, including zero.

I am not 100% sure I have the terminology right. The mantissa may be
normalised in the sense of having no redundant leading zero digits but
in the systems I am describing (ones that don't use binary) there can't
be an implicit leading digit. A normalised binary mantissa always
starts 0.1... so such system don't bother to store the leading 1. As a
result, zero can't be represented with a zero mantissa. On systems with
non binary digits, the leading digit can't be omitted so a zero
mantissa represents zero no matter what the exponent is.

I can't answer your question about efficient equality testing -- you'd
have to ask a hardware designer. However, the fact that there are
redundant representations should not be a surprise. I don't know any FP
format that does not have multiple representations that must compare
equal (+0 and -1 being the most common example).
 
K

Keith Thompson

Ben Bacarisse said:
I can't answer your question about efficient equality testing -- you'd
have to ask a hardware designer. However, the fact that there are
redundant representations should not be a surprise. I don't know any FP
format that does not have multiple representations that must compare
equal (+0 and -1 being the most common example).

I think you mean +0 and -0.
 
K

Keith Thompson

All bits zero isn't necessarily a floating point zero, and it isn't
necessarily a null pointer. Which makes calloc() pretty useless.

To be honest, i don't think it's a big problem. I have code where i
simply 'memset' the whole struct, and i'm sure it's been ported to
twenty machines. No one ever comes back and says 'i ran into that
thing where you memset the struct, it didn't work'.

So there's practical portable versus paranoid portable.

It's something i just don't care about and i think is not worth spending
your time on. My employer doesn't care i don't care because, while
customers may have pretty diverse platforms, they're not geting stuff
from bizarro world.

Architectures can be different, but there really isn't much variation
these days in how basic data types [integer, floating-point number,
pointer] are stored [and how VM basically works].

This is something that people were unsure about 40 years ago; now we
'have the answer'. Such things seem sufficient obvious that you don't
think about it much.

There was a very similar attitude that assumed that dereferencing a null
pointer would give you zero:

int *ptr = NULL;
int *result = *ptr; /* undefined behavior */
assert(result == 0);

because a very popular system happened to guarantee it. A whole lot of
code broke when it was ported to a new system.

There were similar assumptions expressed as "All the world's a VAX"; the
more modern equivalent woudl be "All the world's an x86".

Admittedly there's a more widespread basis for assuming that
floating-point 0.0 and null pointers are represented as all-bits-zero --
but they're still assumptions that are not supported by the standard.

If it turns out that all existing implementatations happen to satisfy
those assumptions, I wouldn't object to adding them as requirements in a
future edition of the C standard. That's exactly what happened with the
assumption that all-bits-zero is a representation of 0 for all integer
types; standards up to and including C99 didn't make that guarantee, but
it was added in one of the Technical Corrigenda and appears in the C11
standard.

Until and unless that happens, I'm not going to count on it -- or if I
do, I'll add a prominent comment documention my assumptions, so future
maintainers will know where to look if things break.
 
J

Johann Klammer

Malcolm said:
So how does the processor do equality comparisons, efficiently?

If the mantissa is not normalized then there are many possible
representations for each number, including zero.

Usually one does an epsilon test.
with epsilon rather small

if(((a+epsilon)<b)&&((a-epsilon)>b))
{

}

The following will most likely evaluate to false on intel machines
The compiler just does a (bitwise)equality test.

a=+0.0;
b=-0.0;

if(a==b)
{

}
 
K

Keith Thompson

Johann Klammer said:
Usually one does an epsilon test.
with epsilon rather small

if(((a+epsilon)<b)&&((a-epsilon)>b))
{

}

That doesn't address the question, which is how the processor performs a
floating-point equality test, the one specified by C's "==" operator.
The following will most likely evaluate to false on intel machines
The compiler just does a (bitwise)equality test.

a=+0.0;
b=-0.0;

if(a==b)
{

}

Only if the compiler is broken. "a==b" yields 1 if a and b are
*numerically* equal, 0 if they're not. If two bit patterns represent
the same value, bitwise comparison will not work.

Oh, and "b=-0.0" won't set b to a floating-point negative zero.
 
J

James Kuyper

So how does the processor do equality comparisons, efficiently?

If the mantissa is not normalized then there are many possible
representations for each number, including zero.

The case that I'm most familiar with, IEEE 754 double precision,
sub-normal numbers have an exponent of 0, a sign bit, and 52 bits of
mantissa, and represent the values (-1)^sign * mantissa* 2^(-1022-52).
The smallest positive value that can be represented is 2^(-1074), the
largest is 2^(-1022) - 2^(-1074).

For normal numbers, 0<exponent && exponent < 0x7FF. The value
represented is (-1)^sign*(1.mantissa)*2^(exponent - 1023). The smallest
positive value represented by this mechanism occurs when mantissa is 0
and exponent is 1, which represents 2^(-1022). That is exactly 2^-1074
bigger than the biggest subnormal value.

The exponent value 0x7FF is reserved for infinities and NaNs.

There doesn't seem to be any overlap in the ranges of normal and
subnormal values, and within each category, all of the non-zero values
that can be represented have a unique representation. A value of exactly
0 must have it's mantissa and exponent both zero, but can have it's sign
bit either set or cleared. As a result, equality comparisons can be
replaced by memcmp(), except for the special handling required by IEEE
754 for +0, -0, infinities and NaNs.
Within this scheme, could you provide an example of the "many possible
representations for each number" that you're referring to?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top