简体   繁体   中英

Struct vs. Function Definitions in Scope

So, as far as I know, this is legal in C:

foo.c

struct foo {
   int a;
};

bar.c

struct foo {
    char a;
};

But the same thing with functions is illegal:

foo.c

int foo() {
    return 1;
}

bar.c

int foo() {
    return 0;
}

and will result in linking error (multiple definition of function foo ).

Why is that? What's the difference between struct names and function names that makes C unable to handle one but not the other? Also does this behavior extend to C++?

Why is that?

struct foo {
   int a;
};

defines a template for creating objects. It does not create any objects or functions. Unless struct foo is used somewhere in your code, as far as the compiler/linker is concerned, those lines of code may as well not exist.

Please note that there is a difference in how C and C++ deal with incompatible struct definitions.

The differing definitions of struct foo in your posted code, is ok in a C program as long as you don't mix their usage.

However, it is not legal in C++. In C++, they have external linkage and must be defined identically. See 3.2 One definition rule/5 for further details.

The distinguishing concept in this case is called linkage .

In C struct, union or enum tags have no linkage . They are effectively local to their scope.

6.2.2 Linkages of identifiers
6 The following identifiers have no linkage: an identifier declared to be anything other than an object or a function; an identifier declared to be a function parameter; a block scope identifier for an object declared without the storage-class specifier extern .

They cannot be re-declared in the same scope (except for so called forward declarations ). But they can be freely re-declared in different scopes, including different translation units. In different scopes they may declare completely independent types. This is what you have in your example: in two different translation units (ie in two different file scopes) you declared two different and unrelated struct foo types. This is perfectly legal.

Meanwhile, functions have linkage in C. In your example these two definitions define the same function foo with external linkage. And you are not allowed to provide more than one definition of any external linkage function in your entire program

6.9 External definitions
5 [...] If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.


In C++ the concept of linkage is extended: it assigns specific linkage to a much wider variety of entities, including types. In C++ class types have linkage. Classes declared in namespace scope have external linkage . And One Definition Rule of C++ explicitly states that if a class with external linkage has several definitions (across different translation units) it shall be defined equivalently in all of these translation units ( http://eel.is/c++draft/basic.def.odr#12 ). So, in C++ your struct definitions would be illegal.

Your function definitions remain illegal in C++ as well because of C++ ODR rule (but essentially for the same reasons as in C).

Your function definitions both declare an entity called foo with external linkage, and the C standard says there must not be more than one definition of an entity with external linkage. The struct types you defined are not entities with external linkage, so you can have more than one definition of struct foo .

If you declared objects with external linkage using the same name then that would be an error:

foo.c

struct foo {
   int a;
};
struct foo obj;

bar.c

struct foo {
    char a;
};
struct foo obj;

Now you have two objects called obj that both have external linkage, which is not allowed.

It would still be wrong even if one of the objects is only declared, not defined:

foo.c

struct foo {
   int a;
};
struct foo obj;

bar.c

struct foo {
    char a;
};
extern struct foo obj;

This is undefined, because the two declarations of obj refer to the same object, but they don't have compatible types (because struct foo is defined differently in each file).

C++ has similar, but more complex rules, to account for inline functions and inline variables, templates, and other C++ features. In C++ the relevant requirements are known as the One-Definition Rule (or ODR). One notable difference is that C++ doesn't even allow the two different struct definitions, even if they are never used to declare objects with external linkage or otherwise "shared" between translation units.

The two declarations for struct foo are incompatible with each other because the types of the members are not the same. Using them both within each translation unit is fine as long as you don't do anything to confuse the two.

If for example you did this:

foo.c:

struct foo {
   char a;
};

void bar_func(struct foo *f);

void foo_func()
{
    struct foo f;
    bar_func(&f);
}

bar.c:

struct foo {
   int a;
};

void bar_func(struct foo *f)
{
    f.a = 1000;
}

You would be invoking undefined behavior because the struct foo that bar_func expects is not compatible with the struct foo that foo_func is supplying.

The compatibility of structs is detailed in section 6.2.7 of the C standard :

1 Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are completed anywhere within their respective translation units, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types; if one member of the pair is declared with an alignment specifier, the other is declared with an equivalent alignment specifier; and if one member of the pair is declared with a name, the other is declared with the same name. For two structures, corresponding members shall be declared in the same order. For two structures or unions, corresponding bit-fields shall have the same widths. For two enumerations, corresponding members shall have the same values.

2 All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

To summarize, the two instances of struct foo must have members with the same name and type and in the same order to be compatible.

Such rules are needed so that a struct can be defined once in a header file and that header is subsequently included in multiple source files. This results in the struct being defined in multiple source files, but with each instance being compatible.

The difference isn't so much in the names as in existence; a struct definition isn't stored anywhere and its name only exists during compilation.
(It is the programmer's responsibility to ensure that there is no conflict in the uses of identically named structs. Otherwise, our dear old friend Undefined Behaviour comes calling.)

On the other hand, a function needs to be stored somewhere, and if it has external linkage, the linker needs its name.

If you make your functions static , so they're "invisible" outside their respective compilation unit, the linking error will disappear.

To hide the function definition from the linker use the keyword static.

foo.c

    static int foo() {
        return 1;
    }

bar.c

    static int foo() {
        return 0;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM