473,659 Members | 2,664 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

forward declarations in C

Hi,

I am trying to implement a tree in C and I have the folowing code:

struct inner {
struct node *left;
struct node *right;
};

struct leaf {
/*data goes here */
};

struct node {
union {
struct inner inner;
struct leaf leaf;
} data;

enum {
INNER,LEAF
}type;
};

I was surprised that this actually compiles, because at the point were I
declare the struct inner there is no struct node. When I add a simple

struct node;

at the beginning it also compiles. My question is now, which is the
correct way to do this kind of thing.

Also if I wanted to add typedefs for inner leaf and node, what would be
the right way to achieve the same thing.

Thanks,
Till

--
Please add "Salt and Pepper" to the subject line to bypass my spam filter

Jun 3 '06 #1
3 9268
On Sat, 03 Jun 2006 16:36:56 +0200, Till Crueger <Ti****@gmx.net >
wrote:
Hi,

I am trying to implement a tree in C and I have the folowing code:

struct inner {
struct node *left;
struct node *right;
};

struct leaf {
/*data goes here */
};

struct node {
union {
struct inner inner;
struct leaf leaf;
} data;

enum {
INNER,LEAF
}type;
};

I was surprised that this actually compiles, because at the point were I
declare the struct inner there is no struct node. When I add a simple
Are you sure you had your warning set to max?

One reason your compiler may choose to accept it is the standard
requires all pointers to any type of struct to have the same
representation. Therefore, when processing struct inner, while the
compiler may not know what a struct node is, it does know everything
it needs to know about a struct node *.
struct node;

at the beginning it also compiles. My question is now, which is the
correct way to do this kind of thing.
The latter, if for no other reason than maybe the lack of diagnostic
is a compiler error and gets fixed in the next update.

Also if I wanted to add typedefs for inner leaf and node, what would be
the right way to achieve the same thing.


One easy way is to insert the word "typedef" at the beginning of each
declaration and the typedef name just before the semicolon, as in
typedef struct inner{...}inner _t;

If you have structure types that point to each other and want to use
the typedef names, you can use
typedef struct inner inner_t;
use inner_t* to your heart's content and later declare what a struct
inner really is.
Remove del for email
Jun 3 '06 #2

Till Crueger wrote:
Hi,

I am trying to implement a tree in C and I have the folowing code:

struct inner {
struct node *left;
struct node *right;
};

struct leaf {
/*data goes here */
};

struct node {
union {
struct inner inner;
struct leaf leaf;
} data;

enum {
INNER,LEAF
}type;
};

I was surprised that this actually compiles, because at the point were I
declare the struct inner there is no struct node. When I add a simple

struct node;

at the beginning it also compiles. My question is now, which is the
correct way to do this kind of thing.

The former works because you can have pointers to
incomplete types. This, consequently, makes the latter
superfluous.

Also if I wanted to add typedefs for inner leaf and node, what would be
the right way to achieve the same thing.


Just add:

typedef struct leaf LEAF;
typedef struct inner INNER;
typedef struct node NODE; (redundant though because we can
consider a node to be external or internal)

--
aegis

Jun 3 '06 #3
>On Sat, 03 Jun 2006 16:36:56 +0200, Till Crueger <Ti****@gmx.net >
wrote:
struct inner {
struct node *left;
struct node *right;
};

struct leaf {
/*data goes here */
};

struct node {
union {
struct inner inner;
struct leaf leaf;
} data;

enum {
INNER,LEAF
}type;
};

I was surprised that this actually compiles, because at the point were I
declare the struct inner there is no struct node.
This is in fact OK. Structure names (in the "struct" namespace)
simply "spring into being" the first time they are mentioned like
this. Where this goes wrong is when the current scope is narrower
than you intended.

When you first mention a structure name, the scope at which the
name "suddenly exists now" is the *current* scope, *whatever that
is*. Thus, at file scope, the struct name has file scope -- which
is the maximum possible scope, so everyone will be able to see it
from here onward. But other scopes exist:

void f(void) {
struct foo { char *p; };
...
}

Here, the scope of "struct foo" is limited to the block that
encloses it. Once we finish with f(), the name vanishes. We can
then create a new and different "struct foo":

void g(void) {
struct foo { double x; };
...
}

and in this case we have done it in another block, so f()'s
"struct foo" and g()'s "struct foo" are entirely separate. This
should be quite familiar, since we can do the same scope tricks
with ordinary variables:

void f2(void) {
double foo;
...
}
void g2(void) {
int foo;
}

Here f2()'s "foo" is completely independent of g2()'s "foo".

Where "the name suddenly springs into being" goes terribly wrong
is when we deal with function prototype scope. Consider, for
instance:

void setname(char *name);
int resolve(int name);

Clearly setname() takes a different kind of "name" from resolve().
The scope of the identifier "name" is limited to each prototype,
so the two "name"s name different variables. But suppose we do this
with structures:

void op1(struct foo *p);
void op2(struct foo *p);

Not only are the two names "p" completely independent, so are the
two "struct foo"s -- just as in the example with f() and g().

The cure for this problem is as Barry Schwarz suggests below:
declare the existence of the structure name in advance, at an
outer scope, so that the inner scope (prototype-scope) names
simply refer back to the already-existing outer-scope name. For
instance:

struct foo;
void op1(struct foo *);
void op2(struct foo *);

Now op1() and op2() both take the same kind of argument.

In article <2f************ *************** *****@4ax.com>,
Barry Schwarz <sc******@doezl .net> wrote:
Are you sure you had your warning set to max?
Since the construct above is legal *and* does what is desired,
it seems appropriate that the compiler did not warn.

[OP writes: with]struct node;

at the beginning it also compiles. My question is now, which is the
correct way to do this kind of thing.


The latter, if for no other reason than maybe the lack of diagnostic
is a compiler error and gets fixed in the next update.


It is not a compiler error but declaring the structure in advance
like this is certainly harmless, and possibly wise. :-)

One other note: a newfangled invention in C89 was to say that, in
an inner scope, a "vacuous" structure declaration like the above
had a new, special property. Suppose you had (for some reason)
code like this:

struct foo { int val; ... other members as needed ... };

void f3(void) {
struct foo { char *p; double q; ... };
...
}

Here, the inner-scope "struct foo" inside f3() overrides the outer
one, so that in f3(), "struct foo" means "f3's user-defined type
foo", not "the file's user-defined type foo". Well, this is all
well and good; but what happens if, for some reason, you want
mutually-referential structures whose scope is limited to f3()?
You might start with:

void f3(void) {
struct bar { struct foo *ref; ... };
struct foo { struct bar *ref; char *p; double q; ... };
...
}

But now "struct bar" refers to the *outer* scope user-defined "foo"
type, rather than the inner-scope one. We can fix this by reversing
the two declarations, but that will fail if there is *also* an
outer-scope user-defined "bar" too. So in C89, as a new committee
invention, they said:

If you write a vacuous declaration for a struct (or union) type
in an inner scope, that "clears the decks" of the outer instance
so that you can then declare a new, inner-scope version.

So, now we can write f3() in complete safety:

void f3(void) {
struct foo; /* vacuous declaration, makes foo local */
struct bar { struct foo *ref; ... };
struct foo { struct bar *ref; char *p; double q; ... };
...
}

Now the user-defined type "bar" in f3() refers to the user-defined
type "foo" in f3(), and the user-defined type "foo" in f3() refers
to the user-defined type "bar" in f3(), just as desired. (We could
be even more "belt and suspenders" about this and write vacuous
declarations for both, but since we *define* a new block-scope
"bar" right away, we do not *have* to declare it here.)
Also if I wanted to add typedefs for inner leaf and node, what would be
the right way to achieve the same thing.


One easy way is to insert the word "typedef" at the beginning of each
declaration and the typedef name just before the semicolon, as in
typedef struct inner{...}inner _t;


I dislike this method because of the limitation you mention:
If you have structure types that point to each other and want to use
the typedef names, you can use
typedef struct inner inner_t;
use inner_t* to your heart's content and later declare what a struct
inner really is.


If you are going to use typedefs at all (and I prefer not to), I
suggest the following sequence:

1) declare that the user-defined type exists, using the
"vacuous declaration" method defined by C89:

struct foo;

2) define the typedef, using the user-defined type just declared:

typedef struct foo StructFoo;

3) repeat for the rest of your types.

Of course, at file scope -- but not always at block scope! -- we
can make use of the fact that user-defined types ("struct"s) simply
"spring into being" at the current scope to combine steps 1 and 2:

typedef struct bar StructBar;

Clearly there are only two possibilities for this line: either
"struct bar" has already been declared at the current (file) scope,
so that StructBar is now an alias for this type; or "struct bar"
has *not* already been declared at this current (file) scope, so
that it springs into being here, and StructBar is now an alias for
this type.

If you engage in the practice of declaring and defining new
user-defined types inside blocks, however, you may get the wrong
type aliased:

void h(void) {
struct foo { int a; };

if (somecond) {
typedef struct foo zog;
struct foo { char *p; };

... code section A ...
}
... code section B ...
}

In "code section A", the name "zog" is an alias for the *outer*
user-defined "foo" type, with one member named "a", while the name
"struct foo" is the name of the *inner* user-defined "foo" type,
with one member named "p". That is, in this code, "zog" and
"struct foo" name two *different* types.

Compare that to the two-part method:

void h(void) {
struct foo { int a; };

if (somecond) {
struct foo;
typedef struct foo zog;
struct foo { char *p; };

... code section A ...
}
... code section B ...
}

Here, in code section "A", "zog" is an alias for the *inner*
user-defined "foo" type, with one member named "p". That is,
in this code, both "zog" and "struct foo" name the *same* type.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Jun 3 '06 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
5462
by: mjm | last post by:
Folks, Please help me with the following problems: ******************************************** 1. I have a class template template<class Base> class Matrix : public Base { /* .... */ }
10
2222
by: Alan Lee | last post by:
Hi i am writing a small simulation for a bunch of atoms jumping around on a surface. I know i am a crappy programmer since i am not very familiar with object oriented languages. I am woindering if anyone can tell me why my forward class declarations not working. the member function findpaths can't seem to access the class Board when I try to pass it a board object by value. I put the Board class latrerbut also put in a forward...
6
5199
by: Steven T. Hatton | last post by:
Should I be able to forward declare something from a namespace different from the current one? For example the following code compiles: //testdriver.hpp #ifndef TESTDRIVER_HPP #define TESTDRIVER_HPP #include <ostream> namespace ns_testdriver{ using std::ostream;
11
2427
by: aleko | last post by:
This applies equally to function prototypes. Why do we have to tell the compiler twice? Other modern compiled languages don't have this requirement. Just curious, Aleko
12
12161
by: fox | last post by:
How do I (portably) make a forward reference to a static (I mean file-scope) variable? I've been using "extern" for years, for example: extern int x; int foo(void) { return x++; }
23
3841
by: mark.moore | last post by:
I know this has been asked before, but I just can't find the answer in the sea of hits... How do you forward declare a class that is *not* paramaterized, but is based on a template class? Here's what I thought should work, but apparently doesn't: class Foo; void f1(Foo* p)
2
508
by: Carlos Martinez Garcia | last post by:
Hi all: I usually make forward declarations in headers. Something like this: class MyClass; Now, I need a reference to a type defined like this (traditional C Style): typedef struct {
6
8613
by: Hunk | last post by:
Hi I have a question on usage of forward declarations of templates. While searching through this group , people suggested using forward declarations and typedefs for templates as // in myfile.h template<typename T,typename R> class some_class;
11
8310
by: Jef Driesen | last post by:
I have the following problem in a C project (but that also needs to compile with a C++ compiler). I'm using a virtual function table, that looks like this in the header file: typedef struct device_t { const device_backend_t *backend; ... } device_t; typedef struct device_backend_t {
0
1467
by: Rune Allnor | last post by:
Hi all. I have these classes, implemented as templates in header files. Right now I have one file per class, where both the declarations and implementations of one class are located in each file. I would like to apply the visitor pattern to some of these classes. One of the technicalities behind the visitor pattern is that one needs forward declarations of classes, since there are two class hierarchies which refer to each other.
0
8427
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8330
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8850
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8626
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7355
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5649
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4175
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
2749
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
1975
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.