What scope are struct members in?

  • Thread starter Johannes Schaub (litb)
  • Start date
J

Johannes Schaub (litb)

The C spec says

"
An identifier can denote an object; a function; a tag or a member of a
structure, union, or enumeration; a typedef name; a label name; a macro
name; or a macro parameter.

The same identifier can denote different entities at different points in the
program.

For each different entity that an identifier designates, the identifier is
visible (i.e., can be used) only within a region of program text called its
scope.
"

and

"
If more than one declaration of a particular identifier is visible at any
point in a translation unit, the syntactic context disambiguates uses that
refer to different entities. Thus, there are separate name spaces for
various categories of identifiers, as follows: [...]
- the members of structures or unions; each structure or union has a
separate name space for its members (disambiguated by the type of the
expression used to access the member via the . or -> operator);
"

Does all that mean that struct members have a scope? Does the following
program declare "x" to have file scope?

struct A {
int x;
};

int main(void) {
}

Some people told me that struct members don't have any scope. But if they
don't have the scope, they are not visible, and if they are not visible,
they cannot be used. But obviously, we can use them. How do struct members
interact with scope?
 
S

Stefan Ram

Johannes Schaub (litb) said:
Does all that mean that struct members have a scope? Does the following
program declare "x" to have file scope?
struct A {
int x;

Structure, union, and enumeration tags have scope that
begins just after the appearance of the tag in a type
specifier that declares the tag.
 
J

Johannes Schaub (litb)

Stefan said:
Structure, union, and enumeration tags have scope that
begins just after the appearance of the tag in a type
specifier that declares the tag.

Huh?
 
B

Barry Schwarz

The C spec says

"
An identifier can denote an object; a function; a tag or a member of a
structure, union, or enumeration; a typedef name; a label name; a macro
name; or a macro parameter.

The same identifier can denote different entities at different points in the
program.

For each different entity that an identifier designates, the identifier is
visible (i.e., can be used) only within a region of program text called its
scope.

Why did you stop quoting here. In two more paragraphs, the answer to
your question is explicitly stated.
"

and

"
If more than one declaration of a particular identifier is visible at any
point in a translation unit, the syntactic context disambiguates uses that
refer to different entities. Thus, there are separate name spaces for
various categories of identifiers, as follows: [...]
- the members of structures or unions; each structure or union has a
separate name space for its members (disambiguated by the type of the
expression used to access the member via the . or -> operator);
"

Why did you bother quoting this. Name space is a different concept
from scope. These paragraphs have nothing to do with your question.
Does all that mean that struct members have a scope? Does the following
program declare "x" to have file scope?

struct A {
int x;
};

int main(void) {
}


An identifier can denote a member of a structure (6.2.1-1)

Every other identifier [not a label name described in the previous
paragraph] has scope (6.2.1-4).

Therefore x has scope regardless of what people tell you.

Since the declaration for x appears outside of any block or list of
parameters, it has file scope (also 6.2.1-4)

Where do find any room to dispute this?
Some people told me that struct members don't have any scope. But if they
don't have the scope, they are not visible, and if they are not visible,

It is possible that this is the first time someone has told you
something incorrect but you can bet the rent it won't be the last.
they cannot be used. But obviously, we can use them. How do struct members
interact with scope?

Exactly as the standard says they do.
 
K

Keith Thompson

Johannes Schaub (litb) said:

That's a direct quotation from the standard, 6.2.1p7.

For example:

{
struct this_is_the_tag {
int x;
};
struct this_is_the_tag obj;
}

The token sequence "struct this_is_the_tag { int x; }" is a type
specifier; it declares the tag "this_is_the_tag". The scope of
"this_is_the_tag" begins just after the appearance of its name,
and ends at the last "}", i.e., at the end of the block.

This means that, for example, you can use use the name
"this_is_the_tag" inside the struct declaration:

{
struct this_is_the_tag {
int x;
struct this_is_the_tag *next;
};
struct this_is_the_tag obj;
}

It's no longer visible (it's out of scope) after the final closing "}".
 
S

Stefan Ram

Keith Thompson said:
That's a direct quotation from the standard, 6.2.1p7.

I was not careful enough: Actually, I wanted to write
about members, not about tags.

The scope of members is not given explicitly, it seems.

From

»the identifier is visible (i.e., can be used) only
within a region of program text called its scope.«,

however, we can deduce, that the scope of a member might be:

- the rest of the body of the definition it is
declared in (except when hidden) and

- the regions after certain operators (like ».« or
»->«), when preceded by an expression of
appropriate type.

(Ignoring the preprocessor, e.g., offsetof.)

In older C dialects, members really had file scope, IIRC!
 
S

Stefan Ram

»the identifier is visible (i.e., can be used) only
within a region of program text called its scope.«,

For example,

struct a { int b; }; int main( void ){ b; }

test.c:1: error: 'b' undeclared (first use in this function)

, doesn't sound as if »b« would be visible in main.

But, of course, gcc is just a compiler.

But ISO/IEC 9899:1999 (E) even explains »visible«:
»can be used« (see quotation above).

»b« cannot be used in main above.

We could add »a.« in front of »b« - but then it would
be another program, so assertions about the scope in
this other program would not be valid for the program
above.
 
K

Keith Thompson

I was not careful enough: Actually, I wanted to write
about members, not about tags.

The scope of members is not given explicitly, it seems.

It doesn't need to be. Note the last sentence:

Structure, union, and enumeration tags have scope that begins
just after the appearance of the tag in a type speciï¬er that
declares the tag. Each enumeration constant has scope that
begins just after the appearance of its defining enumerator
in an enumerator list. Any other identifier has scope that
begins just after the completion of its declarator.

I'm not sure whether the declarator in question is the declaration
of the member or of the struct or union. It might not matter;
I can't think of a legal way to use the name of a struct member
prior to the end of the struct declaration that contains it.
 
J

James Kuyper

The C spec says

"
An identifier can denote an object; a function; a tag or a member of a
structure, union, or enumeration; a typedef name; a label name; a macro
name; or a macro parameter.

The same identifier can denote different entities at different points in the
program.

For each different entity that an identifier designates, the identifier is
visible (i.e., can be used) only within a region of program text called its
scope.
" ....
Does all that mean that struct members have a scope? Does the following
program declare "x" to have file scope?

Yes, and yes.
struct A {
int x;
};

int main(void) {
}

Some people told me that struct members don't have any scope. But if they
don't have the scope, they are not visible, and if they are not visible,
they cannot be used. But obviously, we can use them. How do struct members
interact with scope?

6.2.1p1: "An identifier can denote ... a member of a structure, union,
or enumeration ..."

p2 describes the scope of identifiers which are statement labels.

p3: "Every other identifier has scope determined by the placement of its
declaration (in a declarator or type specifier). If the declarator or
type specifier that declares the identifier appears outside of any block
or list of parameters, the identifier has file scope, which
terminates at the end of the translation unit. If the declarator or type
specifier that declares the identifier appears inside a block or within
the list of parameter declarations in a function definition, the
identifier has block scope, which terminates at the end of the
associated block. If the declarator or type specifier that declares the
identifier appears within the list of parameter declarations in a
function prototype (not part of a function definition), the identifier
has function prototype scope, which terminates at the end of the
function declarator."

p7: after discussing tags and enumeration constants, it says "Any
other identifier has scope that begins just after the completion of its
declarator."

Example:

struct one{
int file_scope_member/* scope starts here */, another;
};

void func(
struct two {
int prototype_scope_member/* scope starts here */,
yet_another;
},
int
/* scope of prototype_scope_member ends here */);

int main(void)
{
struct three{
int block_scope_member/* scope starts here */, the_last_one;
}

return 0;

/* Scope of block_scope_member ends here */}

/* Scope of file_scope_member ends here */
 
E

Eric Sosman

For example,

struct a { int b; }; int main( void ){ b; }

test.c:1: error: 'b' undeclared (first use in this function)

, doesn't sound as if »b« would be visible in main.

But, of course, gcc is just a compiler.

But ISO/IEC 9899:1999 (E) even explains »visible«:
»can be used« (see quotation above).

»b« cannot be used in main above.

We could add »a.« in front of »b« - but then it would
be another program, so assertions about the scope in
this other program would not be valid for the program
above.

You're using "scope" for two notions that the Standard calls
"scope" and "name space." The identifier `b' is in scope everywhere
after its declaration -- including inside main() -- but has meaning
only in the name space of `struct a'. See 6.2.3.
 
J

Johannes Schaub (litb)

Eric said:
You're using "scope" for two notions that the Standard calls
"scope" and "name space." The identifier `b' is in scope everywhere
after its declaration -- including inside main() -- but has meaning
only in the name space of `struct a'. See 6.2.3.

The Standard introduces the concept of "namespaces" as merely a syntactic
construct to disambiguate uses, and not as a semantic entity that somehow
"contains" identifiers.

What does "in the namespace" mean?
 
J

Johannes Schaub (litb)

Johannes said:
The Standard introduces the concept of "namespaces" as merely a syntactic
construct to disambiguate uses, and not as a semantic entity that somehow
"contains" identifiers.

What does "in the namespace" mean?

Ah I think I get this now. The name spaces divide a scope into multiple
sections.
 
J

Johannes Schaub (litb)

Stefan said:
For example,

struct a { int b; }; int main( void ){ b; }

test.c:1: error: 'b' undeclared (first use in this function)

, doesn't sound as if »b« would be visible in main.

But, of course, gcc is just a compiler.

But ISO/IEC 9899:1999 (E) even explains »visible«:
»can be used« (see quotation above).

»b« cannot be used in main above.

We could add »a.« in front of »b« - but then it would
be another program, so assertions about the scope in
this other program would not be valid for the program
above.

No, "b" can be used in "main". But if you just do "b;", it will look into
the ordinary namespace for identifiers. You have to use an "a." or "a->" to
make it look into the struct-A members namespace of the global scope to use
"b" in main.

Just like you have to use "struct a" to use the identifier "a" in main,
which is in the tags namespace of the global scope.
 
J

Johannes Schaub (litb)

Scott said:
Keith Thompson said:
It doesn't need to be. Note the last sentence:

Structure, union, and enumeration tags have scope that begins
just after the appearance of the tag in a type speci?er that
declares the tag. Each enumeration constant has scope that
begins just after the appearance of its defining enumerator
in an enumerator list. Any other identifier has scope that
begins just after the completion of its declarator.

I'm not sure whether the declarator in question is the declaration
of the member or of the struct or union. It might not matter;
I can't think of a legal way to use the name of a struct member
prior to the end of the struct declaration that contains it.

Sounds like a challenge. How about:


struct foo *x;

struct foo {
int a;
char b[ sizeof x->a ];
};


gcc didn't like it; I haven't gone through the standard yet to see if
there's any language to explicitly allow or disallow this.

Interesting. This seems to be allowed/not forbidden.
 
T

Tim Rentsch

Johannes Schaub (litb) said:
Scott said:
Keith Thompson said:
(e-mail address removed)-berlin.de (Stefan Ram) writes:
Stefan Ram wrote:
Structure, union, and enumeration tags have scope that
That's a direct quotation from the standard, 6.2.1p7.

I was not careful enough: Actually, I wanted to write
about members, not about tags.

The scope of members is not given explicitly, it seems.

It doesn't need to be. Note the last sentence:

Structure, union, and enumeration tags have scope that begins
just after the appearance of the tag in a type speci?er that
declares the tag. Each enumeration constant has scope that
begins just after the appearance of its defining enumerator
in an enumerator list. Any other identifier has scope that
begins just after the completion of its declarator.

I'm not sure whether the declarator in question is the declaration
of the member or of the struct or union. It might not matter;
I can't think of a legal way to use the name of a struct member
prior to the end of the struct declaration that contains it.

Sounds like a challenge. How about:


struct foo *x;

struct foo {
int a;
char b[ sizeof x->a ];
};


gcc didn't like it; I haven't gone through the standard yet to see if
there's any language to explicitly allow or disallow this.

Interesting. This seems to be allowed/not forbidden.

Assuming there isn't any text that forbids it, surely
that's just an oversight in the Standard. The operators
'.' and '->' are expected to require complete types, no?
 
H

Harald van Dijk

Johannes Schaub (litb) said:
Scott said:
Sounds like a challenge.  How about:
struct foo *x;
struct foo {
    int a;
    char b[ sizeof x->a ];
};
gcc didn't like it; I haven't gone through the standard yet to see if
there's any language to explicitly allow or disallow this.
Interesting. This seems to be allowed/not forbidden.

Assuming there isn't any text that forbids it, surely
that's just an oversight in the Standard.  The operators
'.' and '->' are expected to require complete types, no?

There is no reason why they should require a complete type, even
though many compilers reject the above code. Referring to a previously
declared member of a not yet completed structure type is possible in
theory, and at least one compiler permits it: OpenWatcom gives no
error, warning, or any other diagnostic for it.
 
J

Johannes Schaub (litb)

Harald said:
Johannes Schaub (litb) said:
Scott Fluhrer wrote:
Sounds like a challenge. How about:
struct foo *x;
struct foo {
int a;
char b[ sizeof x->a ];
};
gcc didn't like it; I haven't gone through the standard yet to see if
there's any language to explicitly allow or disallow this.
Interesting. This seems to be allowed/not forbidden.

Assuming there isn't any text that forbids it, surely
that's just an oversight in the Standard. The operators
'.' and '->' are expected to require complete types, no?

There is no reason why they should require a complete type, even
though many compilers reject the above code. Referring to a previously
declared member of a not yet completed structure type is possible in
theory, and at least one compiler permits it: OpenWatcom gives no
error, warning, or any other diagnostic for it.

I filed a report on clang: http://llvm.org/bugs/show_bug.cgi?id=9471
 
T

Tim Rentsch

Harald van Dij said:
Johannes Schaub (litb) said:
Scott Fluhrer wrote:
Sounds like a challenge. How about:
struct foo *x;
struct foo {
int a;
char b[ sizeof x->a ];
};
gcc didn't like it; I haven't gone through the standard yet to see if
there's any language to explicitly allow or disallow this.
Interesting. This seems to be allowed/not forbidden.

Assuming there isn't any text that forbids it, surely
that's just an oversight in the Standard. The operators
'.' and '->' are expected to require complete types, no?

There is no reason why they should require a complete type, even
though many compilers reject the above code.

I meant 'require' in the sense of 'only guaranteed to be defined
for'.
Referring to a previously
declared member of a not yet completed structure type is possible in
theory, and at least one compiler permits it: OpenWatcom gives no
error, warning, or any other diagnostic for it.

And this behavior is reasonable, or at least permissible, if such
cases were specified as undefined behavior, which is how I expect
the Standard would specify it. So too is giving an error message
and refusing to proceed.
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top