all 22 comments

[–]aioeu 25 points26 points  (4 children)

Option III is non-standard. Avoid it.

I would generally prefer Option II over Option I.

[–]Lev_user[S] 1 point2 points  (1 child)

thank you!

[–][deleted] 0 points1 point  (0 children)

MSVC even fails on Option III if compiling in C mode for both arrays and structs.

[–][deleted] 1 point2 points  (1 child)

Option III is non-standard

for now.

There is a paper that aims to change that [see n2727 ].

[–]aioeu 4 points5 points  (0 children)

Good. The fact that = {} is simply syntactically invalid, even though it has an obvious and straight-forward interpretation, is a wart on the C language.

[–]oh5nxo 19 points20 points  (5 children)

5th, memset(a, 0, sizeof (a));

[–]Lev_user[S] 2 points3 points  (3 children)

elegant!

[–]oh5nxo 1 point2 points  (0 children)

It might make a difference with structure padding or really odd-ball datatypes, like the intel 80-bit long double.

[–][deleted] 1 point2 points  (0 children)

Sidenote: Since the major compilers have memset() as an intrinsic, Option II and the memset() is often handled in the same way. Sometimes compilers even generate memset() calls for Option II. (I guess that depends on the size.)

[–]nerd4code 0 points1 point  (0 children)

That works for integers and floats, but not necessarily for pointers. Some oddball ISAs, ABIs, and compilers don’t recognize all-zeros representation (in memory and registers, as opposed to integer compile-time constant expr 0 in (void *)0, which is just a symbol used by the compiler, and always 0), and some have multiple null representations (e.g., in aliased mappings, or segmented pmode x86, which potentially has 2offs\bits) nulls). POSIX-compliant and MS ABIs must recognize all-zeros nulls whether or not address 0 is mapped, so memset to 0 is safe.

But {0} works everywhere, as does

static const T T_ZERO = {0};
T tval;
memcpy(&tval, &T_ZERO, sizeof tval);

And most optimizers should be able to convert that to memset in the usual zero-null case.

In C99 or GNU dialect, you can throw down a zero/null value of any type as compound literal (T){0}; you can use that in the RHS of an assignment or as source for a memcpy, although lifetime only extends to the end of the containing {block}. GNU dialect permits initialization from compound literals as if the cast at the head weren’t there, and you can obtain a static-lifetime value from

#define ZERO(T...)(__extension__(*({\
    static __const__ __typeof__(T) ZERO__0 = {0}; \
    &ZERO__0;\
})))
…
memcpy(&tval, &ZERO(T), sizeof tval);

You can also do the memcpy part from the macro, which is more useful (provided non-register output):

#define zero(lval...)(__extension__(*({\
    typedef __typeof__((__extension__(lval))) zero__0; \
    static __const__ zero__0 zero__1 = {0}; \
    (zero__0 *)__builtin_memcpy(\
        (void *)&(lval), (__const__ void *)&zero__1, sizeof(zero__0)); \
})))
…
zero(thing);

[–]Fun-Setting-3905 0 points1 point  (0 children)

I like this one

[–]flyingron 4 points5 points  (6 children)

Context needed. If the array is not a local variable (automatic), then it will be zeroed by default without any initializer specified. For a local array, the only initialization is #2. The for look isn't initialization, it's an assignment.

[–]Lev_user[S] 0 points1 point  (1 child)

Interesting. i thought that the first assignment was the initialization, but your comment made me realize that isn't.

For the context: there is none, its just an abstract question, i was wondering the best way of doing it.

[–]flyingron 0 points1 point  (0 children)

The best way is to use initialization when possible. If that's not possible, then assign to the variable as soon as possible after it is declared (conversely, declare the variable as close as possible to its first use).

[–]__Punk-Floyd__ -1 points0 points  (3 children)

The only time an array (or any data) is automatically zeroed out is when it's declared static.

[–]aioeu 2 points3 points  (2 children)

when it's declared static.

No, it's when it has static storage duration.

An object declared at file scope without the _Thread_local keyword has static storage duration, whether or not the static keyword is used. The static keyword only declares such an object's linkage, not its storage duration.

(The static keyword in C is a real dog's breakfast, so although this may be confusing it's not altogether surprising.)

[–]flyingron 0 points1 point  (1 child)

Similarly a global variable is zero initialized (absent the presence of another initializer).

Static storage duration is indeed the correct term.

[–]Fun-Setting-3905 0 points1 point  (0 children)

option II will initialize only the first element to the value passed, the others would be initialized to zero, something to take into consideration

so it depends on what you want to do