Difference between {0} and calloc?

How this answer on another question covers using aggregate initialization

struct foo {
    size_t a;
    size_t b;
};

struct foo bar = {0};

      

causes the built-in types to be initialized to zero.

Is there any difference between using above and using

struct foo * bar2 = calloc(1, sizeof(struct foo));

      

leaving aside the fact that one variable is a pointer.
By looking at the debugger, we can see that both are a

and are b

indeed set to zero for both of the above examples.

What is the difference between the above two examples, are there any problems or hidden problems?

+3


source to share


5 answers


Yes, there is a crucial difference (other than the storage class of your type object struct foo

):

struct foo bar = {0};
struct foo * bar2 = calloc(1, sizeof *bar2);

      

Each member is bar

initialized to zero (and padding is zeroed for a sub-object without an initializer, or if it bar

has static

either thread_local

a storage class),
while all *bar2

are zeroed out, which can have completely different results:

Neither null pointers (T*)0

nor floating point numbers with a value of 0 are guaranteed to be bits-0.
(Actually, only for char

, unsigned char

and signed char

(as well as some additional exact size types from <stdint.h>

) are all-0 bits guaranteed to match the value -0 until some time after C99. Later technical fixes guaranteed it for all integral types.)



The floating point format may not be IEEE754.

(On most modern systems, you can ignore this possibility.)

To quote the c-faq (thanks to Jim Balter for his link ):

Prime 50 series used segment segment 07777, offset 0 for null pointer , at least for PL / I.

+4


source


calloc

gives you a heap of dynamically allocated nullable memory zone (in yours bar2

). But an automatic variable (for example, bar

assuming its declaration inside a function) is allocated on the stack. See also calloc (3)

In C, you need to explicitly specify the allocated memory area free

. But the data allocated to the stack is wiped out when the function returns.

Rerad also has a wikipage on C dynamic memory allocation and garbage collection . Reference counting is a widely used technique in C and C ++ and can be thought of as a form of GC. Think about circular links , they are difficult to deal with.

Boehm's conservative GC can be used in C programs.

Note that the liveness of a memory zone is a global programmatic property. Usually, you cannot claim that a provisioning zone belongs to a particular function (or library). But you can accept agreements about it.



When you code a function that returns a pointer to the heap (i.e. some pointer to dynamic storage), you must document that fact and decide who frees it.

About initialization: the pointer calloc

is zeroed out (when calloc

completed successfully). An automatic variable initialized as {0}

is also set to zero. In practice, some implementations can calloc

differently large objects (by requesting entire zero pages from the kernel for them, such as mmap (2) ) and small objects (by reusing, if available, previously free

-d zones and zeroing out). zero zone uses the fast equivalent of memset (3)

PS. I ignore weird machines where the entire zero-bit memory zone is not cleared data for the C standard, i.e. {0}

... I do not know such machines in practice, even if I know that they are in principle possible (and in theory, a pointer NULL

may not be an all-zero bit)

BTW, the compiler can optimize the entire null local structure (and maybe not allocate it at all on the stack as it will match registers).

+3


source


struct foo bar = {0};

      

Defines an object of type struct foo

named bar

and initializes it to zero.

Zero is defined recursively. All integer subobjects are initialized to 0

, all floating point subobjects to, 0.0

and all pointers to NULL

.

struct foo * bar2 = calloc(1, sizeof(struct foo));

      

IMHO this is better (but equivalent) written as:

struct foo *bar2 = calloc(1, sizeof *bar2);

      

By not repeating the type name, we avoid the risk of inconsistency when the code is changed later.

This dynamically allocates an object of type struct foo

(on the heap), initializes that object to all-bits-zero, and initializes bar2

to point to it.

calloc

may not allocate memory. If so, it returns a null pointer. You should always check this. (The declaration bar

also allocates memory, but if that fails, a stack overflow, and there is no way to deal with it.)

And all bit zero is not guaranteed to be the same as "zero". For integer types (including size_t

) this is almost guaranteed. For floating point and pointer types, it is perfectly acceptable for 0.0

or to NULL

have an internal representation other than all-bit-zero. You are unlikely to run into this, and since all of your structure members are intact, you probably don't need to worry about it.

+3


source


(This answer focuses on initialization differences, in the case struct

containing only integral types)

Both forms are installed a

and b

in 0

. This is because the Standard specifies that all zero bits for an integral type must represent a value 0

.

If there is a struct addition, then the version calloc

sets it, but there may not be a null initialization. For example:

struct foo a = { 0 }, b = { 0 };
struct foo c, d; memset(&c, 0, sizeof c); memset(&d, 0, sizeof d);

if ( memcmp(&a, &b, sizeof a) )
    printf("This line may appear.\n");

if ( memcmp(&c, &d, sizeof c) )
    printf("This line must not appear.\n");

      

The technique that you sometimes see (especially in code designed for low memory systems) is to use memcmp

two structures to compare for equality. When there is padding between the outline elements, it is not reliable because the indentation may vary even though the outline elements are the same.

The programmer did not want to compare structure members separately, as this is too large a code size, so instead he will copy structures with memcpy

, initialize them with memset

; to keep the ability to be used memcmp

to test for equality.


In modern programming, I would strongly advise against doing this; and always use the init form { 0 }

. Another advantage of the latter is that there is no way to mistake the size argument and accidentally set too much memory or too little memory.

+1


source


There is a major difference: the allocation of automatic variables is done at compile time and is provided free of charge (when a stack frame is reserved, there is room.) In contrast, dynamic allocation is done at runtime and has an unpredictable and unsafe cost.

As far as initialization is concerned, the compiler has room for optimization with automatic variables (for example, if not cleared, if not needed); this is not possible with a call to calloc.

If you like the calloc style, you also have the option of doing memset on an automatic variable.

memset(&bar, 0, sizeof bar);

      

UPDATE: Automatic variable allocation is done at compile time.

0


source







All Articles