Reputation: 922
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
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
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
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
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
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
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
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
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
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