Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need to type cast when initializing a user-defined structure using pointer?

I have this structure definition:

typedef struct node_bst
{
  int data;
  struct node_bst *lchild;
  struct node_bst *rchild;
  struct node_bst *parent;
} node_bst;

I tried to create a pointer to the structure using this:

node_bst *root;

and allocated memory to it like this:

root= malloc(sizeof(node_bst));

Now, in order to initialize the data items in it I was tried this statement (taking a cue from the usual initialization of structure variables):

*root= {0, NULL, NULL, NULL};

But the compiler threw off an error

error: expected expression before ‘{’ token

I looked it up and found that I need to typecast it like this:

*root= (node_bst) {0, NULL, NULL, NULL};

Now it works fine but my question is, why do i need to do this?

I expected that the compiler would already know that root is a pointer to node_bst type structure variable. So why the need to typecast the rvalue?

Another strange thing:

int *a= malloc(sizeof(int));
*a= 4;

This works just fine.

like image 306
Nityesh Agarwal Avatar asked Apr 17 '17 18:04

Nityesh Agarwal


2 Answers

There is no casting.

In this statement

*root = (node_bst) {0, NULL, NULL, NULL};

there is used the so-called compound literal (node_bst) {0, NULL, NULL, NULL} that corresponds to an object of the type node_bst and this object is assigned to the object *root.

From the C Standard (6.5.2.5 Compound literals)

3 A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.

Another approach is just to assign each data member of the dynamically allocated object. For example

root->data   = 0;
root->lchil  = NULL;
root->rchil  = NULL;
root->parent = NULL;

As for this statement

*root= {0, NULL, NULL, NULL};

then it is invalid from the C point of view. You may use the braced initialization only in a declaration.

The statement would be valid if you compile the program as a C++ program.

like image 130
Vlad from Moscow Avatar answered Oct 18 '22 07:10

Vlad from Moscow


The syntax in use here is not a typecast but a compound literal. These are defined in section 6.5.2.5 of the C standard:

3 A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.

A compound literal is necessary when assigning to a struct as a whole.

You don't need this syntax in this case:

int *a= malloc(sizeof(int));
*a= 4;

Since *a has type int and 4 is a simple integer constant that can be assigned directly to *a.

Note also that the fact that a pointer is involved is irrelevant. You would need to do the same in this case:

node_bst root;
root= (node_bst){0, NULL, NULL, NULL};

This is different from:

node_bst root = {0, NULL, NULL, NULL};

The former case is an assignment, while the latter is an initialization. An initialization can only be done at the time a variable is defined (even outside of a function), while an assignment can be done at any time.

The syntax for initialization (see section 6.7.9 of the standard) allows for just the brace-enclosed list of values, while a compound literal is needed for an assignment.

Also, as mentioned in the comments, you can still use a compound literal in an initialization, and a compound literal has a lifetime of the current scope whose address you can take.

Here's an interesting example of this:

char *str = (char[]){ "My string" };
str[3] = 'S';

Here, the compound literal is being modified, which is allowed. But if you do this:

char *str = "My string";
str[3] = 'S';

You'll instead be attempting to modify a string literal and will most likely get a segmentation fault.

like image 25
dbush Avatar answered Oct 18 '22 08:10

dbush