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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Nityesh Agarwal
  • 464
  • 2
  • 5
  • 18
  • Nothing to do with pointers - try `node_bst root; root = {0, NULL, NULL, NULL};`. – Oliver Charlesworth Apr 17 '17 at 18:59
  • @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. – Nityesh Agarwal Apr 17 '17 at 19:04
  • 1
    My point is that this behaviour is not specific to pointers. – Oliver Charlesworth Apr 17 '17 at 19:04
  • In case of `*a = 4;`, you don't need a "cast" since the constant of type integer is just a number. – Ajay Brahmakshatriya Apr 17 '17 at 19:05
  • 2
    @NityeshAgarwal key point being - `(node_bst) {0, NULL, NULL, NULL}` is a single entity and not a cast followed by a struct. – Ajay Brahmakshatriya Apr 17 '17 at 19:06
  • That's a compound literal and you better make it `const`. – too honest for this site Apr 17 '17 at 19:14
  • @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? – Nityesh Agarwal Apr 17 '17 at 19:15
  • In this context `(node_bst)` by itself is nothing. It is the `(node_bst)` along with the `{ ... }` that makes up a compound literal. – Ajay Brahmakshatriya Apr 17 '17 at 19:16
  • **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. – too honest for this site Apr 17 '17 at 19:39
  • @chux I was not aware you could take the address of a compound literal. Good to know! – dbush Apr 17 '17 at 19:40
  • @dbush but the lifetime of the literal is only local. So one shouldn't return the address of a compound literal up the call stack. – Ajay Brahmakshatriya Apr 17 '17 at 19:44
  • @dbush Same for _string literal_. Here is [code](http://stackoverflow.com/a/34292061/2410359) that takes advantage of the addressability of a _compound literal_ and the ability to assign its contents – chux - Reinstate Monica Apr 17 '17 at 19:44
  • @Olaf what exactly do you want to make `const`? – Ajay Brahmakshatriya Apr 18 '17 at 03:41
  • @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. – too honest for this site Apr 18 '17 at 09:58
  • @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. – Ajay Brahmakshatriya Apr 18 '17 at 10:05
  • @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. – too honest for this site Apr 18 '17 at 10:14
  • @Olaf that makes sense. Agreed. – Ajay Brahmakshatriya Apr 18 '17 at 10:19

2 Answers2

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 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). – Ajay Brahmakshatriya Apr 17 '17 at 19:24
  • why does `node_bst root= {0, NULL, NULL, NULL};` work? – Nityesh Agarwal Apr 17 '17 at 19:24
  • 1
    @NityeshAgarwal because `T var = ... ;` and `T var; var = ...;` are different. One if initialization and other is declaration and then assignment. Both have different semantics – Ajay Brahmakshatriya Apr 17 '17 at 19:25
  • @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. – Nityesh Agarwal Apr 17 '17 at 19:27
  • Got to love it! Straight forward, succinct and no wasted words `:)` – David C. Rankin Apr 17 '17 at 19:33
  • 1
    @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. – Ajay Brahmakshatriya Apr 17 '17 at 19:34
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.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Can you please expand your answer for why does `node_bst root= {0, NULL, NULL, NULL};` work? – Nityesh Agarwal Apr 17 '17 at 19:24
  • 1
    Thanks for the correction. Edited. @NityeshAgarwal I've edited with details on initialization vs. assignment. – dbush Apr 17 '17 at 19:32
  • 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" };`) – David C. Rankin Apr 17 '17 at 19:38
  • why does an initialization not need a compound literal? – Nityesh Agarwal Apr 17 '17 at 19:38
  • 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... – David C. Rankin Apr 17 '17 at 19:39
  • @DavidC.Rankin the example you showed, is it similar to `char xyz[] = "My string"; char *str = xyz;` ? – Ajay Brahmakshatriya Apr 17 '17 at 19:49
  • 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. `:)` – David C. Rankin Apr 17 '17 at 19:51
  • @dbush. Exactly, that is why I first assigned the string to a char[] and then to *str. This would make *str mutable right? – Ajay Brahmakshatriya Apr 18 '17 at 10:21
  • @AjayBrahmakshatriya Correct. `str` is pointing to a non-const character array, not a string literal, so it can be modified. – dbush Apr 18 '17 at 14:21
  • @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. – Nityesh Agarwal Apr 18 '17 at 14:47
  • 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. – David C. Rankin Apr 18 '17 at 16:45