问题
#include<iostream>
using namespace std;
struct a{
int e;
struct abc *d;
};
struct abc{
int c;
};
int main()
{
return 0;
}
I have defined the struct abc
after definition of struct a
in which i have declared a structure pointer for abc
. This is supposed to throw compilation error because abc
is used before its declaration. But, it doesn't, why?
Whereas when i replace it with just struct abc d
instead of struct abc *d
, it is giving compilation error as expected.
回答1:
This declaration
struct abc *d;
on the one hand declares struct abc
and on the other hand declares pointer d
of type struct abc *
.
In this declaration there is no need to have the exact definition of struct abc because no data member of the structure is used.
This specifier struct abc
is called elaborated type specifier.
It introduces a new type in the given scope or refers to an already declared type.
回答2:
You're right in that, usually, you'd need a forward declaration of such a type:
// Forward declaration
struct bar;
struct foo
{
bar* d; // only a pointer-to-bar; forward declaration sufficient
};
struct bar
{
int c;
};
However, you are (for some reason) using the antiquated idiom of writing struct
before the type name. (This was required in C but has never been in C++.)
struct foo
{
struct bar* d;
};
struct bar
{
int c;
};
Because you write struct bar
there instead of just bar
, that itself counts as a forward declaration of sorts. The compiler now knows that bar
is a type and that's all it needs to know.
It's a bit obscure and subtle, but that's why you do not need the prior forward declaration any more.
[C++11: 3.1/4]:
[ Note: A class name can also be implicitly declared by an elaborated-type-specifier (7.1.6.3). —end note ]
[C++11: 3.3.2/6]:
The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
for a declaration of the form
class-key attribute-specifier-seqopt identifier
;
the identifier is declared to be a class-name in the scope that contains the declaration, otherwise
for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —end note ] [ Note: Other forms of elaborated-type-specifier do not declare a new name, and therefore must refer to an existing type-name. See 3.4.4 and 7.1.6.3. —end note ]
[C++11: 3.4.4/2]:
[..] If the elaborated-type-specifier is introduced by the class-key and this lookup does not find a previously declared type-name, or if the elaborated-type-specifier appears in a declaration with the form:class-key attribute-specifier-seqopt identifier
;
the elaborated-type-specifier is a declaration that introduces the class-name as described in 3.3.2.
回答3:
It works, because the compiler has all the information it needs:
- the size of
d
(= size of pointer)1, and - what
abc
is (it's astruct
).
If you stored an object of type struct abc
instead:
struct abc d;
it would result in an error because the information about the size and memory layout of d
is missing (because struct abc
hasn't been defined yet).
Also if you left out the struct
keyword:
abc *d;
then the information about what abc
is would be missing, and it would be an error as well.
This is supposed to throw compilation error because
abc
is used before its declaration.
It is not used, it is only declared. Using abc
via a pointer would require dereferencing the pointer first (and if abc
is not defined at that point, it would be an error).
1 Pointers to incomplete types are allowed because the size and memory layout of a pointer doesn't depend on what it points to.
回答4:
Pointer declared to incomplete types are allowed .
This is forward declaration-
Forward declaration is a declaration preceeding an actual definition, usually for the purpose of being able to reference the declared type when the definition is not available. Of course, not everything may be done with the declared-not-defined structure, but in certain context it is possible to use it. Such type is called incomplete.
A declaration of the following form
class-key attr identifier ; // struct abc *d; (Your case)
Declares a class type which will be defined later in this scope. Until the definition appears, this class name has incomplete type.
When struct a
is declared it doesn't know the specs of struct abc
yet, but you can forward reference it.
回答5:
In struct a
you refer to a pointer to struct abc
, so the compiler has no need for more information about it to compute the size of the d
member.
In a later stage, it will check that struct abc
is defined, if needed (for exemple if it is dereferenced). This explains the rationale for the standard which
forbids to declare variables of unknown type (
abc
, when not forwarded, is such an unknown type)allows to declare them as pointers to incomplete type. (
struct abc
is an incomplete type: at least it is known to be somestruct
).
Practically, declaring a structure as
struct a {
struct abc *d;
}
amounts to forwarding the declaration of (struct) type abc
, as in
struct abc; // forward
struct a {
abc *d; // legal
};
However the type abc
is incomplete, so the following is illegal
struct z {
struct abc y; // error : incomplete struct type
}
For the curiosity, this is OK:
struct A {
struct B* ptr1; // forwards declaration of B, and use it
B* ptr2; // B is a known (incomplete-)type name now
}
来源:https://stackoverflow.com/questions/31875710/why-doesnt-this-c-code-give-compilation-error