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

2

5

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.

Whited answered 17/4, 2017 at 18:56 Comment(17)
Nothing to do with pointers - try node_bst root; root = {0, NULL, NULL, NULL};.Acromion
@Oliver I am not trying to create a normal variable. I need a pointer to node_bst structure that stores the address of a piece of heap memory.Whited
My point is that this behaviour is not specific to pointers.Acromion
In case of *a = 4;, you don't need a "cast" since the constant of type integer is just a number.Towrey
@NityeshAgarwal key point being - (node_bst) {0, NULL, NULL, NULL} is a single entity and not a cast followed by a struct.Towrey
That's a compound literal and you better make it const.Auspex
@Ajay @Oliver what is this (node_bst) anyways why do i need it? I mean node_bst root= {0, NULL, NULL, NULL}; works as i expected. Why?Whited
In this context (node_bst) by itself is nothing. It is the (node_bst) along with the { ... } that makes up a compound literal.Towrey
Keep that in mind: The compiler knows for the assignment operator. But not for the right side operand of the assignment which is the relevant issue. That applies to all operators and all types.Auspex
@chux I was not aware you could take the address of a compound literal. Good to know!Meristic
@Meristic but the lifetime of the literal is only local. So one shouldn't return the address of a compound literal up the call stack.Towrey
@Meristic Same for string literal. Here is code that takes advantage of the addressability of a compound literal and the ability to assign its contentsLophobranch
@Olaf what exactly do you want to make const?Towrey
@AjayBrahmakshatriya: The compound literal, of course (didn't I say that already?). It is not used for anything than initialisation. Might make no difference for a good compiler, but it is (a) more expressive and (b) "might be" is not a good thing to rely on.Auspex
@Olaf If you are referring to the "might be" I wrote on my last comment on VladFromMoscow's answer I said that because it is upto the compiler. It might or might not occupy space. It also depends on what you do with the compound literal. If you just use for initialization most compilers just assign the corresponding fields. If you take address of the compound literal my compiler puts it on the stack. So it can depend on the code.Towrey
@AjayBrahmakshatriya: No, I refered to the sentence before in my comment. Lifetime is not the only thing. Problem is a bad compiler could do two copy operations: initialiser to literal and literal to *root. Note that compound literals are not constants! In fact even static const does not make a constant, but even not-so-good compilers should handle it that way. and it will help detect problems if e.g. the * on the lhs of the assignment is forgotten. Always head for safe code and support your compiler.Auspex
@Olaf that makes sense. Agreed.Towrey
M
4

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.

Meristic answered 17/4, 2017 at 19:16 Comment(11)
Can you please expand your answer for why does node_bst root= {0, NULL, NULL, NULL}; work?Whited
Thanks for the correction. Edited. @NityeshAgarwal I've edited with details on initialization vs. assignment.Meristic
The other point to make clear is that a compound literal is not limited to use in assignment, it can be used both during assignment and initialization (e.g. char *str = (char[]){ "My string" };)Eft
why does an initialization not need a compound literal?Whited
If you are initializing the exact same declared type, there is no need for a compound literal during initialization, the compiler already knows the correct type...Eft
@DavidC.Rankin the example you showed, is it similar to char xyz[] = "My string"; char *str = xyz; ?Towrey
It is somewhat similar. Generally char *str = "stuff"; initializes a string literal in read-only memory. Using the compound literal changes str to a pointer-to-array-of-chars -- which you can modify freely. :)Eft
@dbush. Exactly, that is why I first assigned the string to a char[] and then to *str. This would make *str mutable right?Towrey
@AjayBrahmakshatriya Correct. str is pointing to a non-const character array, not a string literal, so it can be modified.Meristic
@David Is what's the difference between an array of characters (like the one used in @Ajay 's example), a compound string literal and a normal string literal? By normal string literal i mean the literal in statements like char *str= "My string"; . I know that a normal string literal is placed in the constant memory block.Whited
An array of characters (e.g. char str[] = "My string";) is a traditionally array where all characters (elements of the array) are freely modifiable with storage generally created in the .data section. A string literal is created by creating a pointer to a string (e.g. char *str = "My string";) where the contents of str cannot be modified as storage is created within the .rodata section (read-only data). A compound literal allows you to initialize what would normally be a string literal as a character array in the case of the example I gave. It is a good learning example.Eft
F
10

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.

Footer answered 17/4, 2017 at 19:4 Comment(6)
One small difference between using compound literal and assigning each value individually is that compound literals do have their own memory. As a result you can take address of the compounf literal. (I am unsure about their lifetime).Towrey
why does node_bst root= {0, NULL, NULL, NULL}; work?Whited
@NityeshAgarwal because T var = ... ; and T var; var = ...; are different. One if initialization and other is declaration and then assignment. Both have different semanticsTowrey
@Ajay "compount literals have their own memory" does it mean that when i write the code using compound literals, i am using twice the memory? Once during malloc(sizeof(node_bst)) and then again when i use compound literal.Whited
Got to love it! Straight forward, succinct and no wasted words :)Eft
@NityeshAgarwal I just checked, compound literals have local scope (ie they are allocated on the stack). So yes twice the memory might be used. But you don't have to worry about freeing it.Towrey
M
4

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.

Meristic answered 17/4, 2017 at 19:16 Comment(11)
Can you please expand your answer for why does node_bst root= {0, NULL, NULL, NULL}; work?Whited
Thanks for the correction. Edited. @NityeshAgarwal I've edited with details on initialization vs. assignment.Meristic
The other point to make clear is that a compound literal is not limited to use in assignment, it can be used both during assignment and initialization (e.g. char *str = (char[]){ "My string" };)Eft
why does an initialization not need a compound literal?Whited
If you are initializing the exact same declared type, there is no need for a compound literal during initialization, the compiler already knows the correct type...Eft
@DavidC.Rankin the example you showed, is it similar to char xyz[] = "My string"; char *str = xyz; ?Towrey
It is somewhat similar. Generally char *str = "stuff"; initializes a string literal in read-only memory. Using the compound literal changes str to a pointer-to-array-of-chars -- which you can modify freely. :)Eft
@dbush. Exactly, that is why I first assigned the string to a char[] and then to *str. This would make *str mutable right?Towrey
@AjayBrahmakshatriya Correct. str is pointing to a non-const character array, not a string literal, so it can be modified.Meristic
@David Is what's the difference between an array of characters (like the one used in @Ajay 's example), a compound string literal and a normal string literal? By normal string literal i mean the literal in statements like char *str= "My string"; . I know that a normal string literal is placed in the constant memory block.Whited
An array of characters (e.g. char str[] = "My string";) is a traditionally array where all characters (elements of the array) are freely modifiable with storage generally created in the .data section. A string literal is created by creating a pointer to a string (e.g. char *str = "My string";) where the contents of str cannot be modified as storage is created within the .rodata section (read-only data). A compound literal allows you to initialize what would normally be a string literal as a character array in the case of the example I gave. It is a good learning example.Eft

© 2022 - 2024 — McMap. All rights reserved.