Yannis
Yannis

Reputation: 922

The most efficient way to initialize array member of struct?

I have declared the struct

struct wnode {
  char *word;
  int lines[MAXLINES];
  struct wnode *left;
  struct wnode *right;
};

and the pointer

struct wnode *p;

The pointer is passed to a function. In that function, I first allocate memory for the pointer with malloc. Then I want to initialize the struct member lines to zero zero out the struct member lines.

An array initialization method will not work as it is interpreted as assignment:

p->lines[MAXLINES] = {0};

The compiler throws the error:

error: expected expression before '{' token

In the end, I'm just using a for loop to zero out the lines array:

for (i = 0; i < MAXLINES; i++)
  p->lines[i] = 0;

Is there a better way?

Upvotes: 10

Views: 894

Answers (9)

pmg
pmg

Reputation: 108978

@ikegami's comment to the original question needs to be an answer.

Use calloc() rather than malloc()

struct wnode *p;

// presumably you have N as the number of elements and
p = malloc(N * sizeof *p);
// replace with
//p = calloc(N, sizeof *p);

Upvotes: 0

ryyker
ryyker

Reputation: 23226

The only time an array variable can be initialized in this manner:

int someInt[MAXLINES] = {0};

Is during declaration.

But because this particular variable int lines[MAXLINES]; is declared within the confines of struct, which does not allow members to be initialized, the opportunity is lost to that method, requiring it to be initialized after the fact, and using a different method.

The most common (and preferred) way to initialize after declaration in this case is to use:

//preferred
memset(p->lines, 0, sizeof(p->lines));

A more arduous method, and one that is seen often, sets each element to the desired value in a loop:

for(int i=0;i<MAXLINES;i++)
{
    p->lines[i] = 0;
}

As noted in comments, this method will be reduced by a good optimizing compiler to the equivalent of an memset() statement anyway.

Upvotes: 3

AdamF
AdamF

Reputation: 2940

void *memset(void *s, int c, size_t n)

The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.

The memset() function returns a pointer to the memory area s.

Although, (sizeof(int)*MAXLINES) and sizeof(p->lines) yields the same result in bytes, and both are correct BUT, the second option ( sizeof(p->lines)) is better to use, because if we decide to change the array type or the array size, we dont need to change the expression inside the szieof operator. we change in oneplace only. so we let the compiler to do the work for us!

#include <string.h>  /*for the memset*/
memset(p->lines, 0x0, sizeof(p->lines));

Upvotes: 1

0___________
0___________

Reputation: 67638

If you want to use the = operator, you can do it this way:

struct wnode wn, *p;
/* ........ */
wn = (struct wnode){.word = wn.word, .lines = {0,}, .left = wn.left, .right = wn.right};
*p = (struct wnode){.word = p ->word, .lines = {0,}, .left = p -> left, .right = p -> right};

Upvotes: 5

Spikatrix
Spikatrix

Reputation: 20244

= {0} works only on initialization. You can't use it with assignment as such which is why you get the error.

You can either use a for loop as you said or use memset to zero out the array:

memset(p -> lines, 0, sizeof(p -> lines))

Upvotes: 4

Petr Skocik
Petr Skocik

Reputation: 60067

{0} works only in initialization, not in assignment. Since you can't initialize a pointer target, only assign to it, you can work around the problem by assigning a just-initialized compound literal.

Compilers will usually optimize the compound literal out and assign directly to the target.

{0} initialization of a largish array will frequently compile to a call to memset or equivalent assembly, so another option is to call memset directly on p->lines manually.

Example:

#define MAXLINES 100
struct wnode {
  char *word;
  int lines[MAXLINES];
  struct wnode *left;
  struct wnode *right;
};

//(hopefully) elided compound literal
void wnode__init(struct wnode *X)
{
    *X = (struct wnode){"foo",{0},X,X};
}

//explicit memset
#include <string.h>
void wnode__init2(struct wnode *X)
{
    X->word = "foo";
    memset(X->lines,0,sizeof(X->lines));
    X->left = X;
    X->right = X;
}

https://gcc.godbolt.org/z/TMgGqV

Upvotes: 1

olepinto
olepinto

Reputation: 399

That kind of initialization can only be done on declaration. Notice that in p->lines[MAXLINES] = {0}; the expression p->lines[MAXLINES] means the integer one past the end of p->lines. You could write p->lines[MAXLINES] = 0;. Not correct, but would compile.

You don't have the concept of array any more. You either have p->lines, which is a pointer to int, or p->lines[index], which is an int.

Yes, you have the allocated space, but that's all. memset will do the trick.

By the way, I hope your function (or the caller) do allocate the wnode element...

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

This declaration of a pointer

struct wnode *p;

either zero-initializes the pointer p if the pointer has static storage duration or leaves the pointer uninitialized if the pointer has automatic storage duration, So applying the operator -> to the pointer invokes undefined behavior because the pointer does not point to a valid object.

If to assume that the pointer points to a valid object like for example

struct wnode w;

struct wnode *p = &w;

then within the function you can initialize the data member lines of the object w using the standard C function memset. For example

memset( p->lines, 0, MAXLINES * sizeof( int ) );

You may not write in the function just

p->lines = {0};

because the pointed object is already created and such an initialization is allowed in a declaration of an array. And moreover arrays do not have the assignment operator.

Upvotes: 1

dbush
dbush

Reputation: 224207

Arrays cannot be assigned to directly. You need to either use a loop to set all fields to 0 or you can use memset:

memset(p->lines, 0, sizeof(p->lines));

Note that for non-char types you can only to do this to set all members to 0. For any other value you need a loop.

Upvotes: 6

Related Questions