Home > Net >  Why does this code compile without forward declaration of my struct?
Why does this code compile without forward declaration of my struct?

Time:03-30


struct Figlio
{
    char nome[256];
    struct Genitore* padre;
    struct Genitore* madre;
};

struct Genitore
{
    char nome[256];
    struct Figlio* progenie;
};


int main()
{

    return 0;
}

I expected that the above code does not compile since struct Genitore is not declared before Figlio, so a forward declaration of Genitore (i.e., struct Genitore; on the top) was needed. Instead, in C, it compiles without any problem.

Instead, if I pass a not-yet declared struct pointer to a function, e.g.,

void f(struct st*);

struct st{};

it raises the following warning

warning: ‘struct st’ declared inside parameter list will not be visible outside of this definition or declaration

So, I don't understand why these defferent behaviours and, especially, why forward declarations of struct Genitore; and struct st; respectively are not requested.

CodePudding user response:

This is a matter of scope.

First, for the rule regarding a struct tag, section 6.2.1p7 of the C standard states:

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

Which is what allows the appearance of a struct tag to act as a declaration wherever it appears. This also allows a struct to contain a pointer to itself and have it refer to the same type.

Then the rules for the scope of identifiers is listed in section 6.2.1p4:

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. If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope

Note in particular the two bolded passages. The implicit declaration of struct Genitore when declaring a pointer to that type as a member of struct Figlio has file scope, since it does not appear in a block statement and does not appear in a function declaration, and therefore is visible from that point down in the source file.

In contrast, the implicit declaration that appears in the function declaration is only in scope for the declaration itself. Such a type declaration is problematic because any potential struct pointer that you pass in will refer to a different type than the one implicitly declared in the function declaration.

CodePudding user response:

In this declaration

struct Figlio
{
    char nome[256];
    struct Genitore* padre;
    struct Genitore* madre;
};

you declared two pointers to the incomplete type struct Genitore.

A pointer type is a complete object type.

That is in these declarations you declared the incomplete type struct Genitore and objects of the pointer type struct Genitore *.

The declaration of the structure type struct Genitore is visible in the scope where the structure Figlio is declared (in a file scope or a block scope)

Another example of introducing an incomplete structure type is a declaration it in a typedef declaration for example

typedef struct A A;.

You can define the structure somewhere below after the typedef declaration in the same scope.

Another example is declaring a structure incomplete type in the return type of a function declaration. For example

struct A f( void );

Again you will need to define the structure at least before the function definition or before a function call.

Here is a demonstration program.

#include <stdio.h>

struct A f( void );

struct A { int x; } f( void )
{
    struct A a = { .x = 10 };

    return a;
};    


int main( void )
{
    struct A a = f();

    printf( "a.x = %d\n", a.x );
}

The program output is

a.x = 10

On the other hand, if you will try to dereference a pointer to an incomplete type the compiler will issue an error.

In this declaration

void f(struct st*);

the type specifier struct st has the function prototype scope and is not visible outside the function parameter list.

From the C Standard (6.2.1 Scopes of identifiers)

  1. ...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.
  • Related