1

I am attempting to create an array of pointers to structs. I would like each of the structs to have the same values on initialization, but not the same pointers to them.

Currently I am attempting to do it like this:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

struct Node {
    float value;
    bool evaluated;
    struct Node* children[10];
};

void main() {
    int i;

    struct Node* node = &(struct Node) {
        .value = 0,
        .evaluated = false,
        .children = { 0 }
    };

    for (i = 0; i < 10; i++) {
        node->children[i] = &(struct Node) {
            .value = 0,
            .evaluated = false,
            .children = { 0 }
        };
        printf("Index: %d, pointer: %p\n", i, node->children[i]);
    }
}

Unfortunately, this gives me these results:

Index: 0, pointer: 00DEF640
Index: 1, pointer: 00DEF640
Index: 2, pointer: 00DEF640
Index: 3, pointer: 00DEF640
Index: 4, pointer: 00DEF640
Index: 5, pointer: 00DEF640
Index: 6, pointer: 00DEF640
Index: 7, pointer: 00DEF640
Index: 8, pointer: 00DEF640
Index: 9, pointer: 00DEF640

Is there any way I could produce this in such a way as to give the same results, but with different pointers to each of the structs?

3
  • Is it acceptable to use dynamic memory? That is, malloc and friends? Commented Jul 21, 2022 at 1:08
  • You're initializing pointers to the address of temporary aggregate values. Almost certainly not what you want. Even ignoring that problem, your code doesn't make sense. node->children[i] is the address of the pointer you just initialized to a non-NULL value. Commented Jul 21, 2022 at 1:11
  • @kaylum, It is acceptable to use dynamic memory, as in the other answers, but I would prefer a solution which doesn't resort to it as using dynamic memory will require freeing the memory for an entire tree at the end of my code, which may well be inconvenient. Commented Jul 21, 2022 at 10:28

3 Answers 3

3

A compound literal has a lifetime of the block it is declared in. So after each iteration of the for loop the literal no longer exists and therefore the pointer to it is invalid, and using such a pointer triggers undefined behavior

Rather than using compound literals, you should allocate memory dynamically for each instance. And since you're initializing the members with all zero values, you can use calloc to return zero-initialized memory.

struct Node* node = calloc(1, sizeof(struct Node));

for (i = 0; i < 10; i++) {
    node->children[i] = calloc(1, sizeof(struct Node));
    printf("Index: %d, pointer: %p\n", i, node->children[i]);
}
Sign up to request clarification or add additional context in comments.

2 Comments

calloc() fills it with 0 bytes, which may not be the representation of float 0 or null pointer.
Presumably with this solution I don't really need the loop, I can just use calloc(10, sizeof(struct Node)). The main reason I was hesitant to use dynamic memory was that I will be building a large tree and freeing the memory may well be complicated.
2

When you use a compound literal, the lifetime of the object is the containing block. When you do this in a loop, the lifetime of each array element ends when the loop iteration completes. So these pointers become invalid.

You need to use dynamic allocation with malloc() to create multiple array elements. You can then copy into the allocated memory by assigning from the compound literal.

There's no need to use a compound literal for the top-level node, you can just use an ordinary local variable there.

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

struct Node {
    float value;
    bool evaluated;
    struct Node* children[10];
};

void main() {
    int i;

    struct Node node = {
        .value = 0,
        .evaluated = false,
        .children = { 0 }
    };

    for (i = 0; i < 10; i++) {
        node.children[i] = malloc(sizeof *node.children[i]);
        *(node->children[i]) = (struct Node) {
            .value = 0,
            .evaluated = false,
            .children = { 0 }
        };
        printf("Index: %d, pointer: %p\n", i, node->children[i]);
    }
}

Comments

1

Why do you want to use pointers?

struct Node {
    float value;
    bool evaluated;
    struct {
        float value;
        bool evaluated;
     } child[10];
 } node;

Initialising may be as simple as memset( &node, 0, sizeof( node ) );

Then you can use node.value for the top level, and node.child[i].value for any of the 10 "children".

Use printf( "%d %p\n", i, &node.child[i] ); to confirm that you have 10 distinct 'sub-nodes'.

Do you want each of the 10 "children" to have 10 (unused?) pointers (to grandchildren)?

5 Comments

Indeed, the idea is to create a large tree of nodes to help with a search... There should be grandchildren, great-grandchildren etc
That's a bit scary. The number of 'unused' ptrs will grow exponentially with each generation...
Better than "an array of pointers" would be "a pointer to an array" in your struct. If you malloc() blocks of 10 'children' at once, the 'parent' node's single "children" pointer will store the address of that block. You can still use "children[3]->value" notation. Pointers and arrays are quite similar...
Hmm, that's true... Maybe I should wait longer before allocating that memory to push the growth down. In total I'm going to be searching maybe a couple of million nodes, so this could get messy.
Others may frown on this, but... Unless struct packing is specified, arrays of structs may use 'unreachable' bytes (for alignment of member datatypes.) That 'evaluated' flag may be 'implied' if you keep your array of 10 ptrs, but only malloc and populate the children that you need to... If the particular child ptr == NULL, that child hasn't been set-up yet, and (obviously) not evaluated... Only instantiate & evaluate the subnodes that you need, as you need them. An 'object pool' of "a couple of million nodes" would also save "heap overhead".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.