incorrect warning?

D

Duncan Muirhead

When I compile the program below "Program start" with
gcc -std=c99 -pedantic -o bgcc bgcc.c
(where gcc --version gives
gcc (GCC) 3.4.5 20051201 (Red Hat 3.4.5-2)
Copyright (C) 2004 Free Software Foundation, Inc.
)
I get the warning
bgcc.c:3: warning: "struct thingTag" declared inside parameter list
bgcc.c:3: warning: its scope is only this definition or declaration, which
is probably not what you want

Should the code generate a warning? Is there a way to avoid it other than
adding
typedef struct thingTag * thingP;
and replacing line 3 with
typedef void (*show)( thingP);
(which does shut gcc up)?

TIA
Duncan

Program start
#include <stdio.h>

typedef void (*show)( struct thingTag *);

typedef struct thingTag
{ show f;
double v;
} thing;

void show_metres( thing* p)
{ printf( "%f m\n", p->v);
}

int main( int argc, char** argv)
{
thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 1;
}
 
P

parcour

Why not just do this:

#include <stdio.h>

typedef struct thingTag thing;

typedef void (*show)( thing *);

struct thingTag
{ show f;
double v;

};

void show_metres( thing* p)
{ printf( "%f m\n", p->v);

}

int main( int argc, char** argv)
{
thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 1;
}
 
K

Keith Thompson

Duncan Muirhead said:
When I compile the program below "Program start" with
gcc -std=c99 -pedantic -o bgcc bgcc.c
(where gcc --version gives
gcc (GCC) 3.4.5 20051201 (Red Hat 3.4.5-2)
Copyright (C) 2004 Free Software Foundation, Inc.
)
I get the warning
bgcc.c:3: warning: "struct thingTag" declared inside parameter list
bgcc.c:3: warning: its scope is only this definition or declaration, which
is probably not what you want

Should the code generate a warning? Is there a way to avoid it other than
adding
typedef struct thingTag * thingP;
and replacing line 3 with
typedef void (*show)( thingP);
(which does shut gcc up)?

TIA
Duncan

Program start
#include <stdio.h>

/* HERE */
typedef void (*show)( struct thingTag *);

At this point, the compiler hasn't seen "struct thingTag". It doesn't
know that you're going to declare it later.

You can add an incomplete type declaration before the typedef, at the
point marked /* HERE */ above:

struct thingTag;
typedef struct thingTag
{ show f;
double v;
} thing;

void show_metres( thing* p)
{ printf( "%f m\n", p->v);
}

int main( int argc, char** argv)
{
thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 1;

Why are you returning 1 from your main program? The only portably
defined results are 0, EXIT_SUCCESS, and EXIT_FAILURE. On many
systems (but not all), "return 1;" from main() indicates a failure.

Incidentally, there's no real benefit in creating a typedef for a
structure type. It merely creates an alias "thing" for something that
already has a perfectly good name, "struct thingTag" -- and its an
alias that can't be used as flexibly as the original name.

Here's how I'd probably write it:
========================================
#include <stdio.h>

struct thing;

typedef void (*show)( struct thing *);

struct thing {
show f;
double v;
};

void show_metres(struct thing* p)
{
printf("%f m\n", p->v);
}

int main(int argc, char** argv)
{
struct thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 0;
}
========================================

Though I might be tempted to drop the other typedef as well and write
it like this:
========================================
#include <stdio.h>

struct thing {
void (*f)(struct thing *);
double v;
};

void show_metres(struct thing* p)
{
printf("%f m\n", p->v);
}

int main(int argc, char** argv)
{
struct thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 0;
}
========================================
 
G

Guest

Duncan said:
When I compile the program below "Program start" with
gcc -std=c99 -pedantic -o bgcc bgcc.c
(where gcc --version gives
gcc (GCC) 3.4.5 20051201 (Red Hat 3.4.5-2)
Copyright (C) 2004 Free Software Foundation, Inc.
)
I get the warning
bgcc.c:3: warning: "struct thingTag" declared inside parameter list
bgcc.c:3: warning: its scope is only this definition or declaration, which
is probably not what you want

Should the code generate a warning?

The code is invalid and a diagnostic is required. The warning you
quoted is not required, but tells you exactly what you're doing wrong.
Is there a way to avoid it other than adding
typedef struct thingTag * thingP;
and replacing line 3 with
typedef void (*show)( thingP);
(which does shut gcc up)?

Any way you fix it, you'll have to declare struct thingTag once before
you reference it in show's parameter list. It can be as simple as a
"struct thingTag;" on line 2.
#include <stdio.h>

typedef void (*show)( struct thingTag *);

Here you declare one struct thingTag (let's call it struct A), but its
declaration is only the scope of show's parameter list, which is not
what you want. Your compiler gave a very helpful error message, but you
decided to ignore it. Why?
typedef struct thingTag

Since the original struct thingTag went out of scope, here you declare
(and define) a completely different struct thingTag. Let's call it
struct B.
{ show f;

This is still a pointer to a function taking struct A.
double v;
} thing;

void show_metres( thing* p)
{ printf( "%f m\n", p->v);
}

int main( int argc, char** argv)
{
thing T;
T.v = 1.7;
T.f = show_metres;

This line requires a diagnostic. show_metres is a function taking a
pointer to struct B, and T.f requires a pointer to a function taking
struct A. They are different, and there is no implicit conversion
between them in standard C.
T.f( &T);

This line requires a diagnostic. T.f is a pointer to a function taking
a pointer to struct A, and you pass it a pointer to struct B. They are
different, and there is no implicit conversion between them in standard
C.
return 1;

This may exit successfully on some implementations, unsuccessfully on
others, and perhaps even something in-between on yet more. Are you sure
you don't mean EXIT_FAILURE, or 0 or EXIT_SUCCESS ?
 
D

Duncan Muirhead

/* HERE */


At this point, the compiler hasn't seen "struct thingTag". It doesn't
know that you're going to declare it later.

You can add an incomplete type declaration before the typedef, at the
point marked /* HERE */ above:

struct thingTag;


Why are you returning 1 from your main program? The only portably
defined results are 0, EXIT_SUCCESS, and EXIT_FAILURE. On many
systems (but not all), "return 1;" from main() indicates a failure.


Incidentally, there's no real benefit in creating a typedef for a
structure type. It merely creates an alias "thing" for something that
already has a perfectly good name, "struct thingTag" -- and its an
alias that can't be used as flexibly as the original name.

Here's how I'd probably write it:
========================================
#include <stdio.h>

struct thing;

typedef void (*show)( struct thing *);

struct thing {
show f;
double v;
};

void show_metres(struct thing* p)
{
printf("%f m\n", p->v);
}

int main(int argc, char** argv)
{
struct thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 0;
}
========================================

Though I might be tempted to drop the other typedef as well and write
it like this:
========================================
#include <stdio.h>

struct thing {
void (*f)(struct thing *);
double v;
};

void show_metres(struct thing* p)
{
printf("%f m\n", p->v);
}

int main(int argc, char** argv)
{
struct thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 0;
}
========================================

Thank you, all, for your replies. However I am still somewhat bemused.
If I alter the program by adding
typedef struct
{ struct thingTag* p;
} entity;
before the typedef for show, no warnings are generated. If I add it after
show, show (but not entity) generates warnings. Is this difference in
references to undeclared types in functions and structs an oddity of the
language or the compiler?
TIA
Duncan
 
K

Keith Thompson

Duncan Muirhead said:
Thank you, all, for your replies. However I am still somewhat bemused.
If I alter the program by adding
typedef struct
{ struct thingTag* p;
} entity;
before the typedef for show, no warnings are generated. If I add it after
show, show (but not entity) generates warnings. Is this difference in
references to undeclared types in functions and structs an oddity of the
language or the compiler?

Please trim quoted material when you post a followup. Provide enough
context so we can understand what's going on; that seldom requires
quoting everything.

Here's the original (incorrect) program:
========================================
#include <stdio.h>

typedef void (*show)( struct thingTag *);

typedef struct thingTag
{ show f;
double v;
} thing;

void show_metres( thing* p)
{ printf( "%f m\n", p->v);
}

int main( int argc, char** argv)
{
thing T;
T.v = 1.7;
T.f = show_metres;
T.f( &T);
return 1;
}
========================================

The first occurrence of "struct thingTag" is inside the typedef for
"show". Since there's been no complete declaration of that type, the
compiler treats it as an implicit declaration of a new incomplete
type; normally, such an incomplete type would be completed later. But
in this case, the new incomplete type appears within a function
prototype, and its scope is limited to that prototype. It's just as
if you had declared, within a function:

{
struct incomplete_type;
}
struct incomplete_type {
int x;
int y;
}

The scope of the incomplete declaration is limited to the
brace-enclosed block in which it appears. By the time we see the
complete declaration, the previous incomplete declaration no longer
exists.

When you add your

typedef struct
{ struct thingTag* p;
} entity;

*before* the "show" typedef, you mention "struct thingTag". This is
now treated as a new incomplete type, but since it's not inside the
prototype, its scope extends to the end of the file. (This is
incidental to the declaration of "entity".) Now the compiler sees the
occurrence of "struct thingTag" within the prototype in the "show"
typedef as a reference to the existing incomplete type, rather than as
an implicit declaration of a new one.

If you put the typedef for "entity" *after* the "show" typedef, then
the "struct thingTag" within the prototype declares a new incomplete
type with a limited scope, and the compile warns you about it (because
that's not a useful thing to do). The "struct thingTag" within the
typedef for "entity" *also* declares a new incomplete type (since the
"struct thingTag" from the prototype is out of scope and therefore
doesn't exist), but it's not of such limited scope, so the compiler
doesn't warn you about it.

(This whole business of something being a new declaration if and only
if the thing hasn't already been declared is, IMHO, unfortunate and
confusing, and can mask some serious errors, but there it is.)

If you need a forward declaration of a structure, just do it directly,
not as part of another declaration.

And again, you should seriously consider getting rid of any typedefs
for struct types. By having just one name for the type, you have
fewer things to keep track of.
 
D

Duncan Muirhead

[...] [trimmed]

The first occurrence of "struct thingTag" is inside the typedef for
"show". Since there's been no complete declaration of that type, the
compiler treats it as an implicit declaration of a new incomplete
type; normally, such an incomplete type would be completed later. But
in this case, the new incomplete type appears within a function
prototype, and its scope is limited to that prototype. It's just as
if you had declared, within a function:

{
struct incomplete_type;
}
struct incomplete_type {
int x;
int y;
}

The scope of the incomplete declaration is limited to the
brace-enclosed block in which it appears. By the time we see the
complete declaration, the previous incomplete declaration no longer
exists.

When you add your

typedef struct
{ struct thingTag* p;
} entity;

*before* the "show" typedef, you mention "struct thingTag". This is
now treated as a new incomplete type, but since it's not inside the
prototype, its scope extends to the end of the file. (This is
incidental to the declaration of "entity".) Now the compiler sees the
occurrence of "struct thingTag" within the prototype in the "show"
typedef as a reference to the existing incomplete type, rather than as
an implicit declaration of a new one.

If you put the typedef for "entity" *after* the "show" typedef, then
the "struct thingTag" within the prototype declares a new incomplete
type with a limited scope, and the compile warns you about it (because
that's not a useful thing to do). The "struct thingTag" within the
typedef for "entity" *also* declares a new incomplete type (since the
"struct thingTag" from the prototype is out of scope and therefore
doesn't exist), but it's not of such limited scope, so the compiler
doesn't warn you about it.

(This whole business of something being a new declaration if and only
if the thing hasn't already been declared is, IMHO, unfortunate and
confusing, and can mask some serious errors, but there it is.)

If you need a forward declaration of a structure, just do it directly,
not as part of another declaration.

And again, you should seriously consider getting rid of any typedefs
for struct types. By having just one name for the type, you have
fewer things to keep track of.

Thank you for your informative reply.
If I understand you correctly, there is a special rule for declarations
of function (pointer) types: any any reference to an undeclared type
declares a new (incomplete) type whose scope is limited to the declaration
of the function type. Within struct (and union) declarations the scope of
any new incomplete type is the file. While this seems unfortunate to me,
if that's the way it is, that's the way it is.

As for typedefs for struct types, I suspect your right, but would say:
I, and indeed the people I've worked with, only ever use the typedef'd
names in code and so having the two names is limited to the type
declaration.
Every now and again I've encountered cases where a struct type is changed
(usually to a primitive, very occasionally to a union). In these cases
were a typedef not used, more edits would have to be made -- though
admittedly they were safe edits in that the compiler reports errors for
any edits not done correctly.
Duncan
 
K

Keith Thompson

Duncan Muirhead said:
Thank you for your informative reply.
If I understand you correctly, there is a special rule for declarations
of function (pointer) types: any any reference to an undeclared type
declares a new (incomplete) type whose scope is limited to the declaration
of the function type. Within struct (and union) declarations the scope of
any new incomplete type is the file. While this seems unfortunate to me,
if that's the way it is, that's the way it is.

More or less.

An appearance of "struct foo" refers to an existing type "struct foo"
if it's been declared; otherwise it creates a new incomplete type.

Any declaration has a scope, depending on where it appears, and does
not exist outside that scope. A function prototype is a scope, so if
"struct foo" appears within a prototype, and there's no existing
visible declaration for "struct foo", then the new incomplete type
"struct foo" isn't visible past the end of the prototype. This is
almost certainly useless; thus the warning.

Scopes are explained in section 6.2.1 of the C99 standard; a free
version can be found by googling n1124.pdf.

There are four kinds of scopes: function, file, block, and function
prototype. (Function scope applies only to labels.)
As for typedefs for struct types, I suspect your right, but would say:
I, and indeed the people I've worked with, only ever use the typedef'd
names in code and so having the two names is limited to the type
declaration.

Then I guess you're stuck with it. I find it cleaner to use just the
struct tag, but it's not worth wholesale re-writing of the code. (And
as I said, my opinion on this point is not shared by everyone.)
 
C

Chris Torek

If I understand [Keith Thompson] correctly, there is a special rule
for declarations of function (pointer) types ...

Not exactly. See the article I already referred you to elsewhere in
this thread.
As for typedefs for struct types, I suspect your right, but would say:
I, and indeed the people I've worked with, only ever use the typedef'd
names in code and so having the two names is limited to the type
declaration.

In that case, use the method I outlined in that same article.

Note that you can use the *same* name for the typedef-name and
the structure tag:

struct Foo;
typedef struct Foo Foo;

Note also that you can write "struct Foo;" (at file scope) as many
times as you like, while you can only have the typedef line once.
Hence, in some (relatively rare) situations, you *must* use only
the tag. For instance, if zog.h declares a prototype for a function
zog() that operates on a "Zog", while glik.h declares a prototype
for a function glik() that happens to take a "Zog" as an argument
but -- for whatever reason -- glik.h cannot #include "zog.h" nor
require that those who include glik.h also include zog.h first,
then glik.h must use "struct Zog" instead of "Zog:

/* glik.h */
struct Zog;
void glik(struct Zog *);

/* zog.h */
struct Zog;
typedef struct Zog Zog;

void zog(Zog *);

struct Zog {
Zog *next;
... other fields ...
};

(Of course, everything becomes much simpler if you just throw away
the typedef, and type out the word "struct". Then you no longer
need some sort of typographic gimmick -- in this case, the initial
capital letter -- to distinguish typedef-identifiers from other
ordinary identifiers. Not that a *compiler* needs such a gimmick,
but if you do not use one, programmers will make more mistakes.)
 

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