Array initalization doubt.

K

K.M. Jr.

Hi all,

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Thanks.
 
K

Kevin Bracey

Hi all,

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Yes. In C, an object is either fully initialised or not initialised at all.
If you give an object an initialiser, any elements not explicitly filled
by the initialiser are set to zero.
 
D

dandelion

K.M. Jr. said:
Hi all,

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

The standards guys will answer that definitively. My guess is a resounding
"no" in the sense that I would not count on it. I think only s[0] gets
initialized.

But as I said... Ask the standards guys and hope your compiler conforms to
the standard.
 
C

CBFalconer

Kevin said:
Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Yes. In C, an object is either fully initialised or not initialised
at all. If you give an object an initialiser, any elements not
explicitly filled by the initialiser are set to zero.

For static objects, yes. For automatic objects I would not be so
sure, and I have no intention of dredging through the standard to
find out. It is much easier to simply do whatever initialization
is actually required. This sounds like a possible job for memset.
 
F

Flash Gordon

Kevin said:
Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Yes. In C, an object is either fully initialised or not initialised
at all. If you give an object an initialiser, any elements not
explicitly filled by the initialiser are set to zero.

For static objects, yes. For automatic objects I would not be so
sure, and I have no intention of dredging through the standard to
find out. It is much easier to simply do whatever initialization
is actually required. This sounds like a possible job for memset.

Isn't
void foo(void)
{
char s[30] = { 0 };
/* code */
}
guaranteed to initialise the whole array?
 
T

Thomas Stegen

Flash said:
Isn't
void foo(void)
{
char s[30] = { 0 };
/* code */
}
guaranteed to initialise the whole array?

It is guaranteed by the standard to initialise the whole array.
 
K

Keith Thompson

CBFalconer said:
Kevin said:
Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Yes. In C, an object is either fully initialised or not initialised
at all. If you give an object an initialiser, any elements not
explicitly filled by the initialiser are set to zero.

For static objects, yes. For automatic objects I would not be so
sure, and I have no intention of dredging through the standard to
find out. It is much easier to simply do whatever initialization
is actually required. This sounds like a possible job for memset.

I've just dredged through the standard, and the answer is yes.

C99 6.7.8p14:

An array of character type may be initialized by a character
string literal, optionally enclosed in braces. Successive
characters of the character string literal (including the
terminating null character if there is room or if the array is of
unknown size) initialize the elements of the array.

C99 6.7.8p21:

If there are fewer initializers in a brace-enclosed list than
there are elements or members of an aggregate, or fewer characters
in a string literal used to initialize an array of known size than
there are elements in the array, the remainder of the aggregate
shall be initialized implicitly the same as objects that have
static storage duration.

In the example above, I still wouldn't want to write code that depends
on all 30 elements of s being set to '\0'. The standard guarantees
it, but assuming it is rarely either necessary or useful. If I'm
treating s as a string, I don't care what's after the first '\0'.

A rare case where I would care is when the array, perhaps as part of
of a structure, is being written to a binary file. Keeping the
contents of the file consistent could be useful; zeroing unused
elements could even be important for security reasons. But in that
case, if I've done any manipulation of the array I'm likely to leave
junk in the trailing elements anyway, and I'd probably prefer to zero
it explicitly.
 
T

Thomas Stegen

Christopher said:
Thomas Stegen said:
It is guaranteed by the standard to initialise the whole array.

Now, the question is, does the same hold true for the OP's code?

void foo( void )
{
char s[30]="\0";
/* ... */
}

Yes. The same holds true.
 
J

Jack Klein

K.M. Jr. said:
Hi all,

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

The standards guys will answer that definitively. My guess is a resounding
"no" in the sense that I would not count on it. I think only s[0] gets
initialized.

You don't have to count on it, but the answer is most definitely
'yes'.
 
C

CBFalconer

Keith said:
CBFalconer said:
Kevin said:
(e-mail address removed) (K.M. Jr.) wrote:

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

Yes. In C, an object is either fully initialised or not initialised
at all. If you give an object an initialiser, any elements not
explicitly filled by the initialiser are set to zero.

For static objects, yes. For automatic objects I would not be so
sure, and I have no intention of dredging through the standard to
find out. It is much easier to simply do whatever initialization
is actually required. This sounds like a possible job for memset.

I've just dredged through the standard, and the answer is yes.

C99 6.7.8p14:
.... snip citations etc....

Specifically dealing with automatic storage, it makes no sense to
me. The compiler has to generate code to perform the
initialization, which is a foolishly wasteful endeavor when the
code writer is right there to create such code. Untangling all
those braces and commas to create a herd of assignment statements
does not seem to be the best use of the compiler writers time, nor
of the compiler itself.

Crossed to c.std.c, where maybe someone knows of a liberating
clause that enables what I consider common sense.
 
T

Thomas Stegen

CBFalconer said:
Specifically dealing with automatic storage, it makes no sense to
me. The compiler has to generate code to perform the
initialization, which is a foolishly wasteful endeavor when the
code writer is right there to create such code. Untangling all
those braces and commas to create a herd of assignment statements
does not seem to be the best use of the compiler writers time, nor
of the compiler itself.

Maybe you should just know your language and do:

char s[30];
s[0] = '\0';
 
D

dandelion

Jack Klein said:
K.M. Jr. said:
Hi all,

Consider this line -

char s[30] = "\0";

Does this initialize all array elements to zero ?

The standards guys will answer that definitively. My guess is a resounding
"no" in the sense that I would not count on it. I think only s[0] gets
initialized.

You don't have to count on it, but the answer is most definitely
'yes'.

I noticed.

I still don't count on it for two reasons:

a) If I initialize a structure, I initialize it fully for the sake of
clarity of code.

b) I've had one expirience too much of compiler which *almost* but *not*
quite implement the
entire standard. "Oops... Shit... Sorry. There's a section in the manual
about it on page 652! You
should have read that."

Neither of these reasons is a critique of either the standard, nor the guys
(and that includes you) explaining it, but merely reflects my style of
programming in which clarity of code plays a much more important role than
saving a few characters (or even lines) of code.
 
C

Chris Torek

Regarding initialization of arrays, and the fact that, e.g.,

char s[HUGE_NUMBER] = "";

has to fill all of the elements of s[] with zero:

Specifically dealing with automatic storage, it makes no sense to
me. The compiler has to generate code to perform the
initialization, which is a foolishly wasteful endeavor when the
code writer is right there to create such code. Untangling all
those braces and commas to create a herd of assignment statements
does not seem to be the best use of the compiler writers time, nor
of the compiler itself.

I am not sure what you mean by "untangling all those braces and
commas" -- the original example has none at all, for instance. :)
Crossed to c.std.c, where maybe someone knows of a liberating
clause that enables what I consider common sense.

I am not sure what you "consider common sense" either, but it is
worth noting the historical progression here.

In C89, initializers for automatic aggregates (arrays, structures,
unions, arrays of structures containing unions, and so on) always
had to consist entirely of constant-expressions. That is:

struct S { int a, b; };
struct S static_s = { 1, 2 }; /* static_s.a = 1, static_s.b = 2 */
void c89(void) {
struct S auto_s = { 3, 4 }; /* auto_s.a = 3, auto_s.b = 4 */
...
}

is legal in both C89 and C99. C99 now allows "non-constants" in
automatic aggregate initializers:

void c99(int x, int y) {
struct S auto_s = { x, y }; /* OK in C99, error in C89 */
...
}

If we restrict ourselves to the C89 system, we can immediately see
that *all* aggregate initializers can *always* be compiled "as if"
they were initializing some static instance, plus a call to memcpy()
to copy it to any automatic instance. Clearly static_s can simply
be generated at compile time, as something like:

.data
static_s:
.word 1
.word 2

In c89(), auto_s can be "compiled" as:

c89:
// setup code if any

.rodata
.L1: // initializer for auto_s
.word 3
.word 4
.text

lea .L1,a0 // &static_initializer
lea 4(fp),a1 // &auto_s
mov 8,d0 // sizeof(struct S)
call memcpy // memcpy 8 bytes from .L1 to 4(fp)

Any C89 compiler can do this for all automatic initializers, because
they have the same constraints as static initializers. There is, in
effect, no "code penalty" for arbitrarily large automatic data structures
with initializers -- the same memcpy() that handles a small 4 or 8 byte
structure also handles the 40000 byte zero-filled string. Of course,
there is a (possibly huge) *data* penalty, and at least some compilers
"just happen" to implement this as a call to memset -- for instance,
gcc will handle:

void f(void) {
char line[100] = "hello";
...
}

as something like:

.L1: .asciz "hello"; .align 4
f:
sub 128,sp
lea .L1,a0
lea 8(fp),a1
mov 6,d0
call memcpy // line[0] through line[5] inclusive
lea 14(fp),a0
mov 0,d0
mov 94,d1
call memset // zero out line[6] through line[99]

(the last time I dealt with it, gcc did indeed copy the '\0' that
terminates the string literal, then memset the "default zero" bytes,
even though some cases would benefit from noticing that the implied
'\0' at the end of the string literal is the *same* zero as the
"default" zero, so that we could use .ascii instead of .asciz,
etc.).

C99 removes the "must be constant" constraint, along with adding
anonymous aggregates whose members need not be constant themselves,
so C99 compilers do indeed have to "do more work" than C89 compilers.
The same trick works though: the compiler can call memset() to fill
memory regions with zero bytes (if that suffices; it can call other
runtime support routines to "default initialize" other data types).

Some source fragments are best handled "as if" the aggregate
initializer were simply exploded out into a series of simple
initializations, of course. The obvious degenerate case is a
struct containing a single ordinary data object:

struct temperature { int val; };
struct squarefootage { int val; };
...
void f(void) {
struct temperature = { NOT_TERRIBLY_WARM };
struct squarefootage = { VERY_LARGE_AREA };
...
heat_function(temperature);
area_function(squarefootage);
...
}

There is no need to set one "int" to some value via memcpy(), just
because the user made sure not to pass a temperature to an
area-function.
 
C

CBFalconer

Chris said:
CBFalconer said:
Keith Thompson wrote:

Regarding initialization of arrays, and the fact that, e.g.,

char s[HUGE_NUMBER] = "";

has to fill all of the elements of s[] with zero:
Specifically dealing with automatic storage, it makes no sense to
me. The compiler has to generate code to perform the
initialization, which is a foolishly wasteful endeavor when the
code writer is right there to create such code. Untangling all
those braces and commas to create a herd of assignment statements
does not seem to be the best use of the compiler writers time, nor
of the compiler itself.

I am not sure what you mean by "untangling all those braces and
commas" -- the original example has none at all, for instance. :)
Crossed to c.std.c, where maybe someone knows of a liberating
clause that enables what I consider common sense.

I am not sure what you "consider common sense" either, but it is
worth noting the historical progression here.

In C89, initializers for automatic aggregates (arrays, structures,
unions, arrays of structures containing unions, and so on) always
had to consist entirely of constant-expressions. That is:

struct S { int a, b; };
struct S static_s = { 1, 2 }; /* static_s.a = 1, static_s.b = 2 */
void c89(void) {
struct S auto_s = { 3, 4 }; /* auto_s.a = 3, auto_s.b = 4 */
...
}

is legal in both C89 and C99. C99 now allows "non-constants" in
automatic aggregate initializers:

Let's just take your sample above. For static_s, some initialized
stuff is loaded from the program file, and all is done. For
auto_s, something somewhere has to generate the equivalent of:

/* saw off sufficient storage for auto_s */

auto_s.a = 3; auto_s = 4;

Now lets say auto_s is declared as an array

struct S auto_s{SOMENUM] = {3, 4};

and the same code has to be generated. The problem is whether the
rest of that auto storage need be initialized (which may be a
healthy hit). I claim it makes more sense to leave it
uninitialized. If it does need to be zeroed, say, the code writer
should write something like:

void c89(void) {
struct S auto_s[SOMENUM];

auto_s.a = 3; auto_s.b = 4;
for (i = 2; i < SOMENUM; i++) {
auto_s.a = auto_s.b = 0;
}
...
}

and make it explicit. Or he can zero the area, and then overwrite
with the few required values. Handling all those possible
initializers is a fairly complex job for the compiler.

Note that I am NOT claiming this is how it is.
 
K

Kevin Bracey

In message <[email protected]>
CBFalconer said:
Let's just take your sample above. For static_s, some initialized
stuff is loaded from the program file, and all is done. For
auto_s, something somewhere has to generate the equivalent of:

/* saw off sufficient storage for auto_s */

auto_s.a = 3; auto_s = 4;

Now lets say auto_s is declared as an array

struct S auto_s{SOMENUM] = {3, 4};

and the same code has to be generated. The problem is whether the
rest of that auto storage need be initialized (which may be a
healthy hit). I claim it makes more sense to leave it
uninitialized. If it does need to be zeroed, say, the code writer
should write something like:

void c89(void) {
struct S auto_s[SOMENUM];

auto_s.a = 3; auto_s.b = 4;
for (i = 2; i < SOMENUM; i++) {
auto_s.a = auto_s.b = 0;
}
...
}

and make it explicit. Or he can zero the area, and then overwrite
with the few required values. Handling all those possible
initializers is a fairly complex job for the compiler.


I'll agree with that, having upgraded a C compiler from C90 to C99, and thus
having had to add designated initialisers and non-constant initialisers. The
compiler's job is quite tricky, with automatic objects.

My final approach was:

1) evaluate all constant parts of the initialiser (including the implicit
zeros). Set all non-constant parts to zero.
2) Create a static copy of that constant data.
3) Strip off any trailing zeros. (May leave no static data at all).
4) Check whether a contiguous section at the start of the "trailing zeros"
will be filled in by non-constant initialisers. If these do cover part
or all of the trailing zeros, note that, so we don't end up memsetting
a region that will be entirely dynamically initialised.
5) Generate code that:
i) copies the static copy into the automatic object (if any)
ii) memsets the trailing zeros (if any)
iii) evaluates any non-constant initialisers, and fills them in.

Having looked at that, I realise I'm generating optimal code for:

int fred[20] = { 1, 2, 3, x, y, z, x, y, z };

[ memcpy elements 0-2,
memset elements 9-19,
evaluate elements 3-8 ]

but that I could easily improve:

int fred[20] = { 1, 2, 3, [14] = x, y, z, x, y, z };

[ memcpy elements 0-2
memset elements 3-19 - should be 3-13
evaluate elements 14-19 ]

Obviously, cases like:

int fred[20] = { [3] = x, y, z };

[ memset elements 0-19
evaluate elements 3-5 ]

int fred[20] = { 1, 2, 3, [17] = 4, 5, 6 };

[ memcpy elements 0-19 ]

can't really be much improved on, unless I'm going to generate multiple
memcpys or memsets. Which I don't intend to. The optimisations are mainly
intended to ensure that implicit trailing zeros and entirely dynamically
initialised objects are treated sensibly.
 
F

Flash Gordon

I am not sure what you "consider common sense" either, but it is
worth noting the historical progression here.

In C89, initializers for automatic aggregates (arrays, structures,
unions, arrays of structures containing unions, and so on) always
had to consist entirely of constant-expressions. That is:

struct S { int a, b; };
struct S static_s = { 1, 2 }; /* static_s.a = 1, static_s.b = 2
*/ void c89(void) {
struct S auto_s = { 3, 4 }; /* auto_s.a = 3, auto_s.b = 4 */
...
}

is legal in both C89 and C99. C99 now allows "non-constants" in
automatic aggregate initializers:

Let's just take your sample above. For static_s, some initialized
stuff is loaded from the program file, and all is done. For
auto_s, something somewhere has to generate the equivalent of:

/* saw off sufficient storage for auto_s */

auto_s.a = 3; auto_s = 4;

Now lets say auto_s is declared as an array

struct S auto_s{SOMENUM] = {3, 4};

and the same code has to be generated. The problem is whether the
rest of that auto storage need be initialized (which may be a
healthy hit). I claim it makes more sense to leave it
uninitialized. If it does need to be zeroed, say, the code writer
should write something like:

void c89(void) {
struct S auto_s[SOMENUM];

auto_s.a = 3; auto_s.b = 4;
for (i = 2; i < SOMENUM; i++) {
auto_s.a = auto_s.b = 0;
}
...
}

and make it explicit. Or he can zero the area, and then overwrite
with the few required values. Handling all those possible
initializers is a fairly complex job for the compiler.

Note that I am NOT claiming this is how it is.


That would be relatively simple for arrays, but a right pig for a
complex struct.

It would have been nicer IMHO if you had something like
struct S auto_s[SOMENUM] = { 3 };
not initialising the rest of the struct or array and
struct S auto_s[SOMENUM] = { 3, ... };
initialising the rest.

Then the programmer would be making his/her intent clear and the
implementation could implement it in the most efficient way without
having to spot how to optimise the initialisation code you showed above.

Of course, you could not make this change without risk of breaking an
unknown amount of existing code in a way that might not always be
immediately obvious. Although you could introduce a feature indicating
that the rest of the struct/array *not* be initialise, e.g.
struct S auto_s[SOMENUM] = { 3, _uninitialised };
sets the first element to 3 and does not initialise the rest. This would
allow you to avoid the overhead of initialising a very large
struct/array when you don't want to without breaking existing code.
 
D

Dhruv Ahuja

No, this will only initialise the first element, s[0] to a zero. The
remaining 29 will have a 'garbage' uninitialised value. To set all an
array's elements to zero, you should do this: char s[30] = { 0 );
 
F

Flash Gordon

On 1 Nov 2004 10:45:38 -0800
No, this will only initialise the first element, s[0] to a zero. The
remaining 29 will have a 'garbage' uninitialised value. To set all an
array's elements to zero, you should do this: char s[30] = { 0 );

Please quote enough of the text you are replying to to give some
context. Without that no one has the foggiest idea what you are talking
about. However, I vaguely recall this thread and if it is what I think
it is you are completely wrong as examining the other posts in the
thread will show.
 

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,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top