0

I have been running in circles with this problem. I have a struct declared in extern.h:

typedef struct {
    char data[Q_SIZE];
    int head;
    int tail;
    int size;
}queue;

in main.c I declare two instances of this struct:

queue rxq, txq;

in other .c files, if these structs are used they are declared as global externs ie:

extern queue rxq, txq;

in queue.c there are several functions that accept a pointer to one of these structs as a parameter:

int QGetSize(queue * q)
{
    return q->size;
}

My compiler requires me to prototype these functions, but doesn't like the way I'm prototyping them:

int QGetSize(queue *);
ERROR: parse error at near '*'

int QGetSize(queue);
ERROR: invalid functions argument declaration

int QGetSize(struct *);  // this is the one that to me, should work. the other errors make sense.
ERROR: parse error at near '*'

int QGetSize(struct);
ERROR: parse error at near ')'

int QGetSize(struct queue *);
WARNING: struct declared inside parameter list

it's probably important to note that within the prototype.h file, their is no typedef of queue as this struct, if I try to typedef it or redefine it or even include the .h file where it is originally declared, I get more errors.

ojef
  • 107
  • 1
  • 2
  • 9
  • 3
    For the declarations, the typedef needs to be in scope. (So `#include "queue.h"` or whatever the header is called.) – Daniel Fischer Nov 07 '12 at 22:10
  • if I #include "extern.h" (that is where struct is defined) I get an error within extern.h: "Invalid redefined type name of (queue)" – ojef Nov 07 '12 at 22:13
  • What does it say where the previous definition was, and what is it there? – Daniel Fischer Nov 07 '12 at 22:16
  • oh its because extern.h is included in every file as well as proto.h, so proto.h is re-including extern.h, redeclaring this struct... – ojef Nov 07 '12 at 22:18
  • are you using header guards? – Goz Nov 07 '12 at 22:20
  • took those redundant includes out, and used (queue * ) for parameter, and included extern.h and got a clean compile. thanks @DanielFischer – ojef Nov 07 '12 at 22:23

3 Answers3

3

You can protect against repeated inclusion of the same file with an include guard:

#ifndef FILE_H
#define FILE_H

/* Declarations etc. here. */

#endif

That way you automatically protect against redefinitions like the ones you observed for typedefs. Even though you might often see macros beginning with underscores used as include guards, this is bad practice as those names are in the namespace of the implementation, thus are reserved.

Jens
  • 69,818
  • 15
  • 125
  • 179
  • I'd go a step further, and suggest that ALL include files should have a guard such as this... and +1 for mentioning __GUARD is technically a breach of the language specification – Andrew Nov 08 '12 at 06:20
1

With respect to your compiler errors:

int QGetSize(queue *);
ERROR: parse error at near '*'

This is probably because you're not including extern.h or if you are, the typedef of 'queue' isn't being defined. C gets very confused if it finds a word that should be a type but hasn't (yet) been defined as one.

int QGetSize(queue);
ERROR: invalid functions argument declaration

Same as above. The definition must appear before the use.

int QGetSize(struct *);
ERROR: parse error at near '*'

This is invalid. 'struct' is not a type; 'struct queue' is the type. That is, you need to specify the type tag after 'struct' for it to have meaning.

int QGetSize(struct);
ERROR: parse error at near ')'

Ditto. Also, the compiler is expecting another word after the 'struct' and gets very confused if there isn't one.

int QGetSize(struct queue *);
WARNING: struct declared inside parameter list

This means that type 'struct queue' has not been defined. Which it hasn't. 'typedef struct { ... } queue' is different from 'struct queue { ... }'.

So here's some advice:

Firstly, standard practice is to wrap the body of a header file with an '#ifndef' to ensure it only gets included once:

#ifndef __HDR_H
#define __HDR_H

... code goes here ...

#endif

(__HDR_H has to be unique and is usually a variation of the filename.)

Then, you can always #include the header that defines your types into any file that uses them. The #ifndef (usually) lets you include with impunity.

Secondly, you need to figure out the difference between struct tags and typedefs.

A struct tag is the name of a particular struct definition:

struct queue { ... };       // Defines struct queue

struct queue foo;           // foo is a queue structure.

A typedef is an alias to an existing type:

typedef int HANDLE;         // HANDLE is just another way of saying 'int'

typedef struct queue QUEUE; // QUEUE is shorthand for 'struct queue'

The two are very different things. The first defines a specific type of structure while the second creates a new name for an existing type. You can, in fact, combine them:

typedef struct queue { ... } QUEUE;

and you often want to, especially if 'struct queue' contains a pointer to another 'struct queue'.

So in this:

QUEUE foo;
struct queue bar;

'foo' and 'bar' have exactly the same type.

(Also, semi-related to this: it's common practice in C to make typedef names all uppercase.)

Thirdly, you should use typedef sparingly. In your case, I suggest getting rid of it entirely and just always using the 'struct' keyword. This makes it a lot easier to know what's going on since local struct variables have 'struct' in their declaration so the casual reader can see that it's a struct and not, say, a renamed int.

More importantly, it lets the compiler give you more meaningful error messages. If the struct is undefined and the compiler sees something like this:

int foo(struct bar x);

it knows that bar is a struct and so the whole thing is a parameter declaration and can tell you that 'struct bar' is undefined. If it sees this, however:

int foo(BAR x);

it has no idea what BAR is supposed to be, so the error message tends to be a big WTF?!?!?!?!? instead.

Finally, if you use the 'struct' form, you can pre-declare structs:

struct bar;
int foo (struct bar x);
struct bar { ... };

This is rarely necessary but you'll occasionally find yourself with some really twisted circular dependencies. In that case, this can get you out of a jam. (That's also why the last compiler warning above is a warning and not an error; the compiler interpreted the unknown struct argument as a forward declaration. It's legal but a bad idea.)

Anyway, I hope this helps and good luck.

Chris Reuter
  • 1,458
  • 10
  • 8
  • Never use identifiers beginning with two underscores, as in `__HDR_H`. It's undefined behavior due to invading the implementation namespace. Use `HDR_H` and you are fine. – Jens Nov 09 '12 at 02:40
0

In C, there are two different namespaces of types: a namespace of struct/union/enum tag names and a namespace of typedef names.

typedef struct {
    ...
}queue;

The above declaration declares an anonymous structure and creates a typedef for it. It only has a name in the typedef namespace, but doesn't have a name in the tag namespace. This means it can not be forward-declared. If you want to make a forward declaration, you have to give it a name in the tag namespace.

Please further refer to this https://stackoverflow.com/a/612350/1450257

Community
  • 1
  • 1
wangjunwww
  • 296
  • 2
  • 10