30

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++?

dbush
  • 205,898
  • 23
  • 218
  • 273
V0ldek
  • 9,623
  • 1
  • 26
  • 57
  • 10
    You confuse declarations and definitions. `struct` is a declaration. It declares a new data type, but does not create any new objects. A function, on the contrary, is defined (you create a new function entity). – DYZ May 29 '18 at 18:34
  • Possible duplicate of https://stackoverflow.com/questions/33163028/declaration-vs-definition-in-c – DYZ May 29 '18 at 18:35
  • @DyZ Are you saying that `struct foo` is declared and not defined here? – curiousguy Jun 20 '18 at 16:20

6 Answers6

25

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.

haccks
  • 104,019
  • 25
  • 176
  • 264
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • But if I used `struct foo` in foo.c it would use that definition, and if I used it in bar.c it would use definition from bar, wouldn't it? – V0ldek May 29 '18 at 18:40
  • 2
    @V0ldek, yes, that is correct. If you don't mix them, i.e. not pass a `foo` from foo.c to a function that expects a `foo` from bar.c, you should be ok. – R Sahu May 29 '18 at 18:47
  • So what's the behaviour? The compiler uses the local definition over external? Because surely if I declare `struct foo` twice in the same file I'll get an error. – V0ldek May 29 '18 at 18:50
  • @V0ldek, there is no such thing as external definition of a `struct`. It's only what the compiler sees when compiling a .c file. – R Sahu May 29 '18 at 18:53
  • 2
    In C++ it is a violation of ODR – SergeyA May 29 '18 at 19:04
19

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 (i.e. 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).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Does section 6.9.5 imply that the operands of the `sizeof` and `_Alignof` operators can be externally linked identifiers with _more than one_ definition? Or, is it implying that an operand of these operators need not be a well-defined identifier? That is, an identifier that has been declared, but not defined, can be passed to these operators. – Nicholas Cousar Sep 24 '21 at 19:57
  • 1
    @Nicholas Cousar: It is meant to say that no definition is required. – AnT stands with Russia Sep 25 '21 at 20:15
13

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
4

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
3

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.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • I believe in C it's not undefined to have two incompatible type definitions using the same name, as long as no two declarations using the name refer to the same entity. e.g. no declarations of objects or functions with external linkage that use different definitions of the type. – Jonathan Wakely May 29 '18 at 18:54
  • @JonathanWakely That's what I was getting at with "conflict in the *uses*...", but I guess I was a bit unclear. – molbdnilo May 29 '18 at 18:57
  • Now that I read it again it's clear, sorry for nitpicking what was already correct! – Jonathan Wakely May 29 '18 at 19:01
2

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;
    }
armagedescu
  • 1,758
  • 2
  • 20
  • 31