Initialization of struct to zero with a cast

  • Thread starter Voyageur Galactique
  • Start date
V

Voyageur Galactique

Hi,

Suppose I have a struct T .

struct T* t = malloc(sizeof(struct T));

I want to initialize t to zero.
 
V

Voyageur Galactique

Hi,

Suppose I have a struct T .

struct T* t = malloc(sizeof(struct T));

I want to initialize t to zero.

I did it in the following way :

*t = (struct T) {0};

I would like to know if it is safe ?
From my experience, it works well with gcc, but is it guaranteed to
work on other compiler ?

Thanks
 
V

Voyageur Galactique

Thank you for pointing me this artcile.

I answers one part of my question.

If I well understand, the statement :
struct T t = {0}
will set the fist field to 0 and initialize other fields the same way
it does for static storage.

A practical example is the following :

struct T t_init = {0}, *t = malloc(sizeof(struct T));
*t = t_init;

It is equivalent to :

static struct T t_init;
struct T* t = malloc(sizeof(struct T));
*t = t_init;

But what about the casting technique ?

struct T* t = malloc(sizeof(struct T));
*t = (struct T) {0};

It works, but I'm not sure it should.
 
B

Ben Bacarisse

Voyageur Galactique said:
Suppose I have a struct T .

struct T* t = malloc(sizeof(struct T));

I want to initialize t to zero.

The page that someone else has pointed you to explains that "zero" is,
sadly, slightly ambiguous in this context.
I did it in the following way :

*t = (struct T) {0};

I would like to know if it is safe ?

Yes, that is absolutely safe. It will work even when a null pointer (or
a floating point zero) is not all bits zero.
From my experience, it works well with gcc, but is it guaranteed to
work on other compiler ?

The trouble is that it's C99 -- the newest C standard -- and not all
compilers implement C99.
 
B

Ben Bacarisse

Voyageur Galactique said:
But what about the casting technique ?

struct T* t = malloc(sizeof(struct T));
*t = (struct T) {0};

It works, but I'm not sure it should.

For a conforming C99 compiler yes. Technically it's not a cast. The
construct:

(type-name){ initialisers }

is called a compound literal. It's effect is to make an un-named object
of the given type. A cast produces just a value whereas a compound
literal is an object you can take the address of.

<snip>
 
V

Voyageur Galactique

Technically it's not a cast. The construct:
(type-name){ initialisers }

is called a compound literal. It's effect is to make an un-named object
of the given type. A cast produces just a value whereas a compound
literal is an object you can take the address of.

Thank you, it completely answers my question.
I'm very pleased to learn it's something else than a cast. I couldn't
understand how it could ever work.
 
V

Voyageur Galactique

struct T* t = malloc(sizeof(struct T));
use calloc instead?

Tom

Of course not. My question was misleading. I wanted to say,
"initialize *t to zero", or more precisely "intialize all fieds in t*
struct to zero"
 
V

Voyageur Galactique

I thank you all for your instructive answers.

Just a few pieces informations I found about compound literals
regarding portability :

- "C++ supports this feature as an extension to Standard C++ for
compatibility with C"
quoting :
http://publib.boulder.ibm.com/infoc....doc/language/ref/clrc02compound_literals.htm

- "As an extension, GCC supports compound literals in C90 mode and in C
++."
quoting :
http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Compound-Literals.html

- Chances are it is not supported in MSVC but I didn't tried.

To finish, an alternate solution to add to my collection :
found in :
http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Compound-Literals.html

struct T* t = malloc(sizeof(struct T));
{
struct T temp = {0};
*t = temp;
}
 
T

Tom St Denis

Of course not. My question was misleading. I wanted to say,
"initialize *t to zero", or more precisely "intialize all fieds in t*
struct to zero"

memset(t, 0, sizeof *t);

Tom
 
V

Voyageur Galactique

memset(t, 0, sizeof *t);

Tom

You are right it works! But is it guaranted to work everytime
everywhere? I what taught the bit representation didn't always reflect
what appears to a 0 for the user.

struct T {
int _int;
double _double;
struct {
char _char;
float _float;
} sub;
};

int main(void) {

struct T* t = malloc(sizeof(struct T));
memset(t, 0, sizeof(struct T));
printf("%d, %f, %i, %f \n", t->_int, t->_double, t->sub._char, t-
sub._float);

struct T* s = calloc(1, sizeof(struct T));
printf("%d, %f, %i, %f \n", s->_int, s->_double, s->sub._char, s-
sub._float);
}



Are you it is supposed to work ?
 
K

Keith Thompson

Voyageur Galactique said:
Of course not. My question was misleading. I wanted to say,
"initialize *t to zero", or more precisely "intialize all fieds in t*
struct to zero"

Right, that's what calloc() does: it allocates space (as if by a
call to malloc), sets that space to all-bits-zero, and returns a
pointer to the newly allocated space. It's equivalent to calling
malloc() followed by memset().

But unless your struct T consists entirely of integer members
(possibly nested within sub-structs and so forth), there's no
guarantee that all-bits-zero is a representation of zero for
the type. (The guarantee that all-bita-zero is a representation
of zero for integer types didn't appear until after C99, in
one of the Technical Corrigenda, but the committee felt free
to add this guarantee because it was already satisfied by all
(?) implementations.)

(And you're checking the result of malloc(), right?)
 
V

Voyageur Galactique

(And you're checking the result of malloc(), right?)

Yes. Actually I want to dynamically allocate memory for pointers
contained in a struct and I'm looking for an easy way to release all
memory in case of malloc failure.
I know it is a bit remote with the starting topic of this thread, but
here is an example :
Please leave me your comments if you want.


typedef struct Foo {
char * pt_1;
float * pt_2;
int** pt_3;
/* (...) a lot of pointers */
int val_1;
/* (...) a lot of variables */
} Foo;

void del_foo(Foo* t);

Foo* new_foo()
{
int i;
Foo* t = malloc(sizeof(Foo));
if (!t) goto cleanup;
*t = (Foo) {0};

t->pt_1 = malloc(sizeof(char)*100);
if (!t->pt_1) goto cleanup;

t->pt_2 = malloc(sizeof(float));
if (!t->pt_2) goto cleanup;

t->pt_3 = calloc(sizeof(int*), 42);
if (!t->pt_3) goto cleanup;
for(i = 0; i < 42; i++)
{
t->pt_3 = malloc(sizeof(int));
if (!t->pt_3) goto cleanup;
}

/* (...) memory allocation for other pointers*/

return t;

cleanup:
if (t) del_foo(t);
return NULL;
}

void del_foo(Foo* t)
{
int i;
free(t->pt_1);
free(t->pt_2);
if (t->pt_3) {
for(i = 0; i < 42; i++)
{
free(t->pt_3);
}
}
free(t->pt_3);

/* (...) freeing memory allocation from other pointers*/

free(t);
}
 
V

Voyageur Galactique

Ah I forgot, regarding what was already said :

the statement is not guaranted to work :
t->pt_3 = calloc(sizeof(int*), 42);

it should be replaced with :
t->pt_3 = malloc(sizeof(int*) * 42);
for(i = 0; i < 42; i++) {
t->pt_3=NULL;
}
 
V

Voyageur Galactique

Yes. Actually I want to dynamically allocate memory for pointers
contained in a struct and I'm looking for an easy way to release all
memory in case of malloc failure.
I know it is a bit remote with the starting topic of this thread, but
here is an example :
Please leave me your comments if you want.

[snip example]

You might find it worh the effort to put together a little storage
management utility that I call a storage pool kit.  (No doubt there is
a better name.)

The basic idea is to refine the world's simplest storage allocator. It
works like this:  We start with a block of unallocated storage and and
a pointer to the beginning of unallocated storage.  When we get a
request for storage we return the current value of the pointer and
advance the pointer to the new start of unallocated storage.  (But do
make allowance for alignment.)  We never free anything - once storage
is allocated it stays allocated.

This scheme is simple and cheap; allocation is trivial and there are
no deallocation costs.  Eventually it fails when all of the available
storage is allocated.  Suppose, however, we have a series of
allocations that will/can all be released at the same time.  Then our
primitive allocator works very well.  As it happens, this is a very
common pattern. Your struct that holds pointers to arrays that must be
allocated in turn is a good example.

It turns out that there can be many such sequences in the code;
therefore it is better to have a separate storage pool for each such
sequence of allocations.  The storage pool manager must have three
main entry points, open, get, and close.  Open creates a storage pool
and returns a handle to the pool.  Get gets a block of storage from a
given pool (specified by its handle).  Finally, close frees all of the
malloced storage used by the pool and marks the handle as inactive.

The basic idea in the implementation is to use a linnked list of
buffers.  When the current buffer is exhausted "get" gets a new muffer
from malloc and adds it to the list.  Each new buffer is (a) large
enough to hold the request, and (b) larger than the previous buffer by
some factor.  (I use 1.5, but 2 is okay too.)

As a note, the buffer free space pointer must always be an aligned
address.

I have an implementation of this kind of utility on the web.  There is
a .c file and a .h file.  They are at:
<http://home.tiac.net/~cri_a/san/source_code/utl/src/stgpool.c>
<http://home.tiac.net/~cri_a/san/source_code/utl/include/stgpool.h>

You probably don't want to use my implementation; it is part of a
package of utilities and has calls to other functions in the package.
However you can adapt the code to create your own stand alone version.
The needed modifications are pretty obvious.

The exception is error handling.  There are three kinds of errors to
take into account.  One is malloc failure.  (In my version getspace
handles malloc failures.)  A stand alone implementation should handle
malloc failures.  The obvious thing to do is for "get" to call "close"
and then return a null pointer.  This will free all of the buffers and
may inactive the pool handle.

The second kind of error is a faulty handle.  It could be a stale
handle or something that was never a handle.  Unfortunately the
reference implementation doesn't check for these kinds of errors; it
uses the "trust that the user gets it right" policy.

The final kind of error is a failure of the "open" function.  This
could either be because of a bad argument or because of a memory
failure.

I will leave the issue of error handling as an open question.
Finally, I'm not really happy with the handle format but that is a
topic for another time.

Hi Richard,
thank you for your long and very contructive answer. As a beginner, I
think I will need some time to study your implementation seriously.
However just a question, is your implementation 64bits safe and
endianness safe?
Cheers
 
K

Keith Thompson

On Feb 13, 8:29=A0am, (e-mail address removed) (Richard Harter) wrote: [snip]

Hi Richard,
thank you for your long and very contructive answer. As a beginner, I
think I will need some time to study your implementation seriously.
However just a question, is your implementation 64bits safe and
endianness safe?

The short answer is yes. The right thing to ask about is alignment.
The code assumes that addresses divisible by 8 (that's 64 bits) are
aligned for all types. AFAIK that is true for all popular platforms.
If you want 128 bit alignment set NABITS to 4.

Note that there's no portable way to determine whether a given
address satisfies a particular alignment (unless you already know
that it's relative to the start of a malloc()ed block). On most
systems, you can assume that converting an address to an integer
type gives you meaningful results -- but I've worked on systems
where this assumption doesn't hold.
 

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,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top