简体   繁体   中英

C structure pointer to a structure array, from a structure

I need some help with the pointer syntax. I have an array of structures, and I'm trying to create a pointer to it, from inside another structure, contained in an array.

struct foo array* = malloc(sizeof(foo)*10);
bar_arr[i]->foo_ptr = &array;

I have read through this question: c pointer to array of structs . While it did clear up some of the confusion, I'm still left with a few errors to solve.

I have defined my structure like this:

struct bar{
...
struct foo (*foo_ptr)[];
...
}

It seems that I am able to add new structures to the array, as the following returns no errors:

(*bar_arr[i]->foo_ptr)[j] = new_struct;

However, if I attempt to create a new pointer to a struct inside of the struct array like this:

struct foo* one_ptr = (*bar_arr[i]->foo_ptr)[j];

or this:

struct foo* one_ptr = bar_arr[i]->foo_ptr[j];

the compiler will give me errors. Either one for invalid use of an array with unspecified bounds, or that one_ptr and foo_ptr[j] have incompatible types (in this case: one is of type foo and the other is struct foo *).

Is it simply not possible to create a pointer to one of the elements of the structure array? Any help would be appreciated.

EDIT: BETTER CODE EXAMPLE

I am currently working with a linearly linked list for use as a shared memory stack, for use with pthreads. The array of structures is a shared pool of nodes. Whenever a node is removed from the stack, it is put into the shared pool. If a thread attempts to add a new item to the stack, it checks whether or not the element to be added exists in the pool, to avoid having to allocate new memory every time it needs to add another element to the stack.

Each thread needs a pointer to the shared pool of nodes as an argument. That's why i'm trying to create a pointer to it from the argument structure. I was hoping that the question would have a simple answer.

typedef struct node{...} node_t;

struct thread_args
{
...
node_t (*pool)[];
...
}

node_t shared_pool = malloc(sizeof(node_t)*10);
arg[index]->pool = &shared_pool;

I am trying to access the pool inside one of the functions that the thread is executing.

node_t *item;
item = stack->root;
//remove item from stack
(*args->pool)[index] = item;
//...later
node_t *item2;
item2 = (*args->pool)[index];

Hopefully this provides some more information.

Also, the exact error i get from attempting to use:

node_t *pool;
args[index]->pool = shared_pool;

is as follows: error: incompatible types when assigning to type 'struct node_t *' from type 'node_t'.

For starters this declaration (not mentioning the omitted semicolon after the closing brace)

struct bar{
...
struct foo (*foo_ptr)[];
                     ^^^
...
}

is invalid. You have to specify the size of the array. Otherwise the pointer will point to an object of an incompete type.

Taking into account how you are trying to use the array this declaration does not make sense. You could declare the data member like

struct bar{
...
    struct foo *foo_ptr;
...
}; 

and then

struct foo *array = malloc(sizeof(struct foo)*10);
bar_arr[i]->foo_ptr = array;

In this case you could write

bar_arr[i]->foo_ptr[j] = new_struct;

and

struct foo* one_ptr = bar_arr[i]->foo_ptr + j;

or

struct foo* one_ptr = &bar_arr[i]->foo_ptr[j];

This is wrong:

struct foo array* = malloc(sizeof(foo)*10);
bar_arr[i]->foo_ptr = &array;

Assuming you mean

struct foo *array = malloc(sizeof(foo)*10);
bar_arr[i]->foo_ptr = &array;

you are storing the address of the actual array variable into your structure's foo_ptr variable. From the code snippet you posted, that's likely a local variable for the function, so it's not only the wrong type, it ceases to exist once your function returns.

Given struct foo , this will allocate a pointer to an array of struct foo :

struct foo *array = malloc( nelem * sizeof( *array ) );

and this will assign that

bar_arr[i]->foo_ptr = array;

Or, more directly:

bar_arr[i]->foo_ptr = malloc( nelem * sizeof( *( bar_arr[i]->foo_ptr ) ) );

Note that I use a dereference to the pointer the malloc() result is being assigned to as the argument to sizeof() . That's a simple way to ensure that the pointer and the memory allocation done always refer to the same type.

The call to malloc() will allocate nelem contiguous copies of struct foo (which will contain unknown, unspecified data...). Those copies can be accessed via

array[ n ]

as long as n >= 0 and n < nelem .

Is it simply not possible to create a pointer to one of the elements of the structure array?

You're making your code too complex. To create a pointer to the n th element:

struct foo *nthElement = array + n;

You simply add n to the address of the first element, and the resulting pointer will refer to the n th element, assuming n is within the proper range.

Short answer

The declaration of foo_ptr in struct bar is not right. Change it into struct foo *foo_ptr . And change your assigning statement to the follows

struct foo *array = malloc(sizeof(struct foo)*10);
bar_arr[i]->foo_ptr = array;

Then you can read or write the element of the array like below

// read from array
struct foo one_struct = bar_arr[i]->foo_ptr[j];

// write to array
struct foo new_struct = { ... }
bar_arr[i]->foo_ptr[j] = new_struct;

( NOTE : the both read and write operation are copy of all fields of the struct foo ).

Explanation of the changes

Think about an easy example. How do you make an array of 10 char .

char *a = malloc(sizeof(char)*10)

a is a pointer of char , it points to a space that contains 10 char . So we can use it as an array. a[4] means the 5th char of the array of 10 char .

What if you want to assign this array to another variable?

char *b = a;

Now b is an array too (not a copy of the original array, b and a point to one array). You can refer to the element via b now, just like what you do with a . Eg, b[4] .

Nobody will declare b as char (*b)[] right?

More explanation of your mistakes

Still start with an simple example. When we see such a declaration -- char (*x)[10] -- what would we say about x ? We may say x is a pointer to an array of 10 char .

Let's look at your declaration, struct foo (*foo_ptr)[] . Likewise, we may say, foo_ptr is a pointer to an array of ... length-unspecified struct foo . (Notice the difference in the length of array. Actually it is wrong not to specify the length, I'll talk about it soon)

Based on this declaration of yours, let's talk about the errors you encountered when using it.

Statement 1 that throws error

struct foo* one_ptr = (*bar_arr[i]->foo_ptr)[j];

To simplify and make it clear, I'm going to remove the part of bar_arr[i]-> in following discussion.

In this statement, *foo_ptr is an array of length-unspecified struct foo , we can call it this way, *foo_ptr is an array of struct foo [] . So (*foo_ptr)[j] means the j th element of the array of struct foo [] . Everything seems good here. BUT your array has no length defined, even though you assigned a malloc'ed array to it, but in compile-time, gcc has no idea about it. That is why it throws an error about "unspecified bounds".

Statement 2 that throws error

struct foo* one_ptr = bar_arr[i]->foo_ptr[j];

As I said above, foo_ptr is a pointer to an array of struct foo [] . Let's pay attention to the fact that foo_ptr is a pointer first. So in this statement, foo_ptr[j] is kinda like *foo_ptr . Other than the index j , they have same type. Since we already know *foo_ptr is an array of struct foo [] , foo_ptr[j] is also an array of struct foo [] . It's an array , not an object of struct foo . To put it precisely, you need to imagine there is a big array, of which each element is still array (array of struct foo [] ). foo_ptr[j] is just the j th element, or say j th array, in this big array.

So the practice you assign foo_ptr[j] to one_ptr is more or less like the following example

typedef char char_array_of_4_t[4];
char_array_of_4_t array1;
char *p = array1;        //ERROR: incompatible types

To make it easy to understand, I show you another exmaple below.

char array2[4];
char *p = array2;

In the second example, there is no error. In perspective of human being, array1 and array2 are both array of 4 char , however, in mind of gcc , array1 is a type of array , while array2 is a type of pointer .

In this declaration ...

struct bar{
    // ...
    struct foo (*foo_ptr)[];
    // ...
};

... member foo_ptr of struct bar is declared as a pointer to an unknown-length array of struct foo . This is allowed, but it is unusual. One would ordinarily just declare a pointer to the first element of the array, as this plays out more naturally in C, and it's simpler, too:

struct bar{
    // ...
    struct foo *foo_ptr;
    // ...
};

You can assign to the members of the pointed-to array via this form:

bar_arr[i]->foo_ptr[j] = new_struct;

(provided of course, that the pointed-to array has at least j + 1 elements. To get a pointer to such an element, you would do either

struct foo *struct_ptr = &bar_arr[i]->foo_ptr[j];

or, equivalently,

struct foo *struct_ptr = bar_arr[i]->foo_ptr + j;

You can do the same with your original struct declaration, but the form would need to be slightly different. For example:

struct foo *struct_ptr = &(*bar_arr[i]->foo_ptr)[j];

But really, make it easier on everybody, yourself included, and don't do that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM