struct array within struct

T

TiloVillwock

Hi,

I have a problem with arrays within a struct definitions. Let's say I
want to define a tree structure with tree nodes and each node can have
several child nodes, like this:

typedef struct _node {
char *name;
struct _node *parent;
struct _node *children
} node;

Now when I create a tree like this:

node root = {"root node\0"};
node n1 = {"n1\0"};
node n2 = {"n1\0"};
node n3 = {"n1\0"}:
node children[] = {n1, n2, n3};
root.children = children;

and browse through it like this:

int i;
for (i = 0; i < 20; i++) {
printf("node #%i: %s\n", root->children.name);
}

it has far more entries than I initially added. I thought there should
actually occur a segfault for any entry greater than 3. Why is this?

Also no matter how many children I add, sizeof ( e.g. sizeof(root--
children) ) always returns 3 as a result, which is obviously the size
of the struct rather than the array. Any Thoughts?

Thanks in advance!

Tilo
 
S

SG

I have a problem with arrays within a struct definitions. Let's say I
want to define a tree structure with tree nodes and each node can have
several child nodes, like this:

typedef struct _node {
    char *name;
    struct _node *parent;
    struct _node *children
} node;

Note: Your struct definition doesn't include any arrays, just
pointers.
Now when I create a tree like this:

node root = {"root node\0"};
node n1 = {"n1\0"};
node n2 = {"n1\0"};
node n3 = {"n1\0"}:
node children[] = {n1, n2, n3};
root.children = children;

The \0 is actually not necessary. Also, since you're not allowed to
change string literals, you should avoid initializing non-const char
pointers so they point to the first character of a string literal.
and browse through it like this:

int i;
for (i = 0; i < 20; i++) {
    printf("node #%i: %s\n", root->children.name);
}

it has far more entries than I initially added. I thought there should
actually occur a segfault for any entry greater than 3. Why is this?


Undefined behaviour is undefined behaviour. Only when you're lucky,
you get a segfault. There is no segfault guarantee ... because it's
*undefined* behaviour.
Also no matter how many children I add, sizeof ( e.g.
sizeof(root-->children) ) always returns 3 as a result,

Does it? The member 'children' in in node is a pointer. So,
sizeof(root->children) should be the same as sizeof(node*). Apart from
the C99 VLA extension, sizeof is a compile-time operator that checks
the static type only -- in this case the size of a pointer to node.
which is obviously the size of [...] rather than the array.
Any Thoughts?

That's the way it is. It's just a pointer -- pointing to the first
element of the array. If you want to know the length of the array you
have to remember it somewhere in some form. Don't confuse pointers
with arrays.

Your local 'children' is actually an array of type node[3], not
node[]. The compiler just counts the number of elements in the
initializer list for you and *completes* the type to be node[3].

I hope you're aware of the fact that in your little program all the
node objects and arrays have an "automatic life-time" -- that is they
cease to exist when the program execution leaves the block they are
defined in just like any other local int variable, for example. --
just in case you're used to other languages with implicit indirections
and garbage collection.
Thanks in advance!

Tilo

Cheers,
SG
 
E

Eric Sosman

Hi,

I have a problem with arrays within a struct definitions. Let's say I
want to define a tree structure with tree nodes and each node can have
several child nodes, like this:

typedef struct _node {
char *name;
struct _node *parent;
struct _node *children

Missing a semicolon here. (Demonstrating that you have not shown
us actual code, but some kind of inaccurate paraphrase. My further
comments are about the code you've shown; if they do not apply to the
actual code you're having trouble with, that's your fault and not mine.)

By the way: Don't use the identifier _node at file scope (outside
a function), because it's a reserved name that the implementation may
be using for its own purposes. If those purposes clash with yours,
it's your fault for misappropriating a reserved name -- think of it as
identity theft. Advice: Don't use _ at the start of an identifier
(this advice is stronger than strictly necessary, but easy to follow).
} node;

Now when I create a tree like this:

node root = {"root node\0"};
node n1 = {"n1\0"};
node n2 = {"n1\0"};
node n3 = {"n1\0"}:

Is there some special reason you need *two* '\0' bytes at the
end of each string? The C compiler will append one '\0' on its
own (except in one odd case that doesn't apply here), which is
why sizeof("Hello") is six, not five.

Also, you may find it confusing that all child nodes have
the same name, like the five sons of George Foreman, all named
"George."
node children[] = {n1, n2, n3};

This will only work inside a function (that is, not at file
scope), and even then it won't do quite what you probably want.
You've already got four structs: root,n1,n2,n3. This creates
three more, children[0],children[1],children[2], and *copies*
their content from n1,n2,n3. So now you've got seven nodes:
one parent and two copies of each child.

If you want the children to be in an array, you've got to
put them there to start with. Get rid of n1,n2,n3 and write

node children[] = { {"n1"}, {"n2"}, {"n3"} };
root.children = children;

There are a few problems here (or "near here"). One is that
you haven't finished initializing things: From your struct layout
it looks like each child is supposed to point to its parent -- but
all your children have NULL as the parent pointer.

A bigger problem is that you've given yourself no way to know
how many children a node has. Are there three? Two? Forty-two?
None at all? There's nothing in the data to let you know (well,
the "none at all" case is distinguishable, but ...). There are
many ways to do this; two of the commonest are:

- The parent node has an element that counts its children,
so you'd write something like

root.children = children;
root.kidcount = sizeof children / sizeof children[0];

- The array of children is followed by a "sentinel," a child
that's distinguishable from the others as "I'm not really
a child," much like the '\0' at the end of a string. In
your example this might be a node with an empty name, so

node children[] = { {"n1"}, {"n2"}, {"n3"}, {""} };

... and when traversing the children of a node you'd know
to stop when you found one with "" as its name.
and browse through it like this:

int i;
for (i = 0; i< 20; i++) {
printf("node #%i: %s\n", root->children.name);
}

it has far more entries than I initially added. I thought there should
actually occur a segfault for any entry greater than 3. Why is this?


Accessing outside the bounds of an array is undefined behavior.
Note that word "undefined:" it means there are no promises at all
about what will happen. In particular, there is no promise that
your program will generate a trap or fault of any kind -- it might,
of course, or it might do something even weirder, or it might even
seem to "work." There are no guarantees, good or bad.
Also no matter how many children I add, sizeof ( e.g. sizeof(root--
of the struct rather than the array. Any Thoughts?

I'm not sure what you mean by `sizeof(root--children)', because
as far as I can see it shouldn't compile. The number 3 is also
puzzling; I don't see anything in your code that is likely to have
a sizeof equal to 3 (although surprises are possible). But again:
We know you've not shown us your actual code, so we're all just
guessing at what you really mean.

However: None of your nodes contains any array; all of your
nodes have exactly the same size. Each node contains three pointers
(a char* and two struct _node*), and that's that. These pointers
aim at data that is not inside the node, and does not contribute
to the sizeof the node.
 
N

Nick Keighley

     I'm not sure what you mean by `sizeof(root--children)', because
as far as I can see it shouldn't compile.

I think he said
sizeof (root-->children)
which also makes no sense
 The number 3 is also
puzzling; I don't see anything in your code that is likely to have
a sizeof equal to 3 (although surprises are possible).

that confused me too
 
T

TiloVillwock

First of all, thanks for all the advice. Most of the stuff wasn't that
obvious to me, as I have experience with other object oriented
languages and just started with C.

with sizeof(root-->children) I actually meant to write sizeof(root-
children) which was supposed to return the children array of the root
element and to determine its size with sizeof.

My question now is: Is there anyway to have an array of the same
struct type it is nested in and get the number of elements of this
array with e.g. sizeof or do I have to count them everytime I add
another child?

Thanks again.

Tilo
 
E

Eric Sosman

First of all, thanks for all the advice. Most of the stuff wasn't that
obvious to me, as I have experience with other object oriented
languages and just started with C.

with sizeof(root-->children) I actually meant to write sizeof(root-
element and to determine its size with sizeof.

(Too bad about the line break just before the >, which makes
the continuation look like a line quoted from some other message.)

sizeof(root->children) won't compile, either, since `root' is
not a pointer. sizeof(root.children) is valid, but it gives the
number of bytes in the `children' element of the `root' struct.
What is the `children' element? It is a pointer, so its size is
whatever the size of a pointer is on your machine. Observe that
the size of a pointer is not (in general) the size of the thing
it points at.
My question now is: Is there anyway to have an array of the same
struct type it is nested in

No. A struct cannot contain an instance of itself. In the
real world, you cannot put one Volkswagen Beetle in the back seat
of an identical Volkswagen Beetle, for much the same reason.
and get the number of elements of this
array with e.g. sizeof or do I have to count them everytime I add
another child?

How you manage your arrays and counts and whatnot is up to you.
But it's *entirely* up to you: C isn't going to give you much help.

It seems to me you misunderstand what an array is, or at least
have a confused notion of how arrays and pointers are related. The
comp.lang.c Frequently Asked Questions (FAQ) at <http://www.c-faq.com/>
devotes all of Section 6 to this topic; I suggest you read it before
you try to go much further.

I'd also suggest that the approach you seem to be taking ("Try
random stuff until something seems to work") is not a particularly
efficient search procedure. You're guessing, it seems to me, about
the nature of the tool you're trying to use -- and when you guess about
how a band saw works, you may end up with fewer fingers than you had
originally. Get a C textbook or tutorial or some such, and read it --
don't reason "I've written Smalltalk; I'll just fake my way through C."
 
T

TiloVillwock

     No.  A struct cannot contain an instance of itself.  In the
real world, you cannot put one Volkswagen Beetle in the back seat
of an identical Volkswagen Beetle, for much the same reason.

Ok that makes sense. But i could have a pointer to another node
struct, right? So why not pointing to an array of nodes?
don't reason "I've written Smalltalk; I'll just fake my way through C."

That was the plan :D
But no worries, I got Kernighan & Ritchie on my desk. But I just tried
to get all the information from it without reading the entire book.
Seems like I need to do this after all.
 
T

Thad Smith

TiloVillwock said:
Ok that makes sense. But i could have a pointer to another node
struct, right? So why not pointing to an array of nodes?

You certainly can point to an array of nodes. As Eric said earlier, you need to
have a way of knowing how many nodes are in the array.
 
E

Eric Sosman

Actually, it would probably compile. It would be the same value as
sizeof(a>b)

Since `root' is a variable of type `struct _node', the compiler
might be hard-pressed to think of a way to post-decrement it ...
 
K

Keith Thompson

Please don't snip attribution lines (like the one above that says
follow the discussion, and give proper credit to the people you're
quoting.
Ok that makes sense. But i could have a pointer to another node
struct, right? So why not pointing to an array of nodes?

Yes, you can have a pointer to an array, but it's rarely a good idea.

C code that deals with arrays usually uses a pointer to the first
element of the array, combined with some other method to determine
the number of elements in the array. The pointer itself doesn't
tell you you anything about the length of the array, which is why
you need to use some other method to keep track of that.

For example, the predefined functions that work with strings
generally take char* arguments, which just point to the first
character of the string. The length of a string is determined by
scanning for the null character ('\0') that defines the end of it.

For your application, you probably want to keep a count in your
structure, logically associated with the pointer to the first
element.

The relationship between arrays and pointer in C is one of the
biggest stumbling blocks for people learning C. Section 6 of the
comp.lang.c FAQ, <http://www.c-faq.com>, gives the best explanation
I've seen.
 
N

Nick Keighley

Ok that makes sense. But i could have a pointer to another node
struct, right? So why not pointing to an array of nodes?


That was the plan :D
But no worries, I got Kernighan & Ritchie on my desk. But I just tried
to get all the information from it without reading the entire book.
Seems like I need to do this after all.

perhaps not the entire book straight away, but certainly the bits on
arrays and pointers. OTH K&R is a pretty slim book... Arrays aren't
first class objects in C. If you "pass an array to a function" in C
what really happens is that a pointer to the first element gets
passed. Infomation about the size of array is /not/ passed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top