4

Here's the setup:

foo.h:

typedef struct my_struct {
  int a;
} my_struct;

const my_struct my_struct1; 
my_struct my_struct2;

foo.c:

#include "foo.h"
const my_struct my_struct1 = { .a = 1 };
my_struct my_struct2 = { .a = 2 };

main.c:

#include "foo.h"
#include <stdio.h>
int main() {
    printf("%d %d\n", my_struct1.a, my_struct2.a);
    return 0;
}

Which when compiled with gcc main.c foo.c prints 1 2. The question is, haven't I declared multiple variables with the same name (the two sets of structs)?

edit: Thanks for the reply all. I see I may have posed a slightly confusing question. Originally I thought const may have implied some sort of extern declaration (which makes no sense, I know), which is why I thought to create my_struct2. Much to my surprise, it still works.

Ben
  • 2,065
  • 1
  • 13
  • 19
  • 1
    See [How do I use `extern` to share variables between source files in C](http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c/). You can stop before the 'Late Addition'. Rule of thumb: variables declared in headers should always be prefixed with `extern`. (Rule for experts only: Think twice more before breaking the rule — and don't cry when your code breaks after you broke the rule.) – Jonathan Leffler Mar 31 '15 at 07:10

2 Answers2

2

According to the C Standard (6.9.2 External object definitions)

1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

2 A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

Thus in your example these declarations of identifiers in header foo.h itself included in module foo.c

const my_struct my_struct1; 
my_struct my_struct2;

are not their external definitions because they do not have initializers.

These objects are externally defined only in module foo.c itself

const my_struct my_struct1 = { .a = 1 };
my_struct my_struct2 = { .a = 2 };

where they are explicitly initialized.

In module main.c these external declarations constitute tentative definitions and zero initialized.

According to the Appendix J

J.5.11 Multiple external definitions 1 There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

Thus the behaviour of the program is undefined unless your compiler supports the extension described in the Appendix J.

You should set specifier extern for these identifiers in header foo.h that the declarations in main.c would not constitute tentative definitions.

The one declaration rule is applied to identifiers that have no linkage. (6.7 Declarations)

3 If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except that a typedef name can be redefined to denote the same type as it currently does and tags may be redeclared as specified in 6.7.2.3.

In your example all identifiers have external linkage. So they may be declared several times but defined only once.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • *external definition* not to be confused with *declaration with `extern` storage class* – chqrlie Mar 31 '15 at 06:37
  • 1
    You can't afford to omit the material represented by the ellipsis: _…If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0._ – Jonathan Leffler Mar 31 '15 at 06:46
  • 1
    You also need to be aware of the 'common extensions' listed in Annex J.5, and specifically **J.5.11 Multiple external definitions** _There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2)._ – Jonathan Leffler Mar 31 '15 at 06:49
  • @Jonathan Leffler It is unimportant because in his example there is already an external definition. So the continuation about the tentative definitions of the quote is not important. – Vlad from Moscow Mar 31 '15 at 06:50
  • 1
    It's important because `main.c` defines the two structures with no initializers, so they are default initialized to zeroes, and `foo.c` defines the two structures with initializers. This means the variables are defined twice (once in `main.o` and once in `foo.o`), and by the ODR, the program should fail to link with doubly-defined variables. The header should contain `extern` in front of the variable definitions so that they are declarations, and then there'd be nothing to debate. As it stands, the code relies on J.5.11 to work, and J.5.11 is non-standard, even though it is common. – Jonathan Leffler Mar 31 '15 at 06:52
  • You also need to know about §6.2.2 Linkages of identifiers. _¶2 In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function._ – Jonathan Leffler Mar 31 '15 at 07:04
  • Also, §6.9 External definitions says: _An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. 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._ – Jonathan Leffler Mar 31 '15 at 07:04
  • I've been through this discussion, or very similar ones, before, and — while I don't have the location of everything memorized — I have had to do the searching and I know that the material is not all as close together as you'd like (but fixing that is hard, so the standard is what it is). – Jonathan Leffler Mar 31 '15 at 07:06
  • Getting there. The code in `main.c` has actual definitions of the two structure per §6.9.2. They're tentative until the end of the TU, but then they become non-tentative and zero-initialized. Thus, there are officially two full definitions of the variables, one in `foo.o` and one in `main.o`. But J.5.11 says "some compilers will let you get away with it", and if the OPs code links on his machine, then his compiler is one of those that supports the J.5.11 extension to the standard. – Jonathan Leffler Mar 31 '15 at 07:15
  • @Jonathan Leffler The appendix J.5.11 is confusing. I think that it has to include word explicitly "or more than one is EXPLICITLY initialized, the behavior is undefined (6.9.2). – Vlad from Moscow Mar 31 '15 at 07:20
  • Yes: that's so that: `int i = 9;` in `file1.c` and `int i = 10;` in `file2.c` mean that `file1.o` and `file2.o` cannot be linked into a single program, even under J.5.11. In the question, the variables in `main.o` are implicitly initialized to zero, but they are fully defined. J.5.11 says that because the ones in `foo.o` are explicitly initialized and the ones in `main.o` are not, it is OK to link the object files if J.5.11 is implemented by the compiler/linker (but it is not following the standard strictly). – Jonathan Leffler Mar 31 '15 at 07:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/74172/discussion-between-vlad-from-moscow-and-jonathan-leffler). – Vlad from Moscow Mar 31 '15 at 07:31
  • @Jonathan Leffler Thanks. I hope that the current edition of my post is now correct is not it? – Vlad from Moscow Apr 01 '15 at 09:08
0
const my_struct my_struct1; 

here my_struct1 is a constant object of type my_struct. I hope you know what is a constant variable.

my_struct my_struct2;

Here my_struct2 is a object of type my-struct.

So to sum it up these are 2 different objects and have separate memory allocated for them so there is no mutiple definitions for the same object you are defining 2 different objects which is totally fine.

Gopi
  • 19,784
  • 4
  • 24
  • 36
  • 2
    This is not the question: `my_struct1` and `my_struct2` are defined, not just declared in `foo.h`. `foo.c` contains an initialized definition while `main.c` contains just the *common* uninitialized definition. These multiple structure definitions coexist and refer to same instances at link time thanks some some explicit magic in the linker to support legacy software. You should avoid such constructs in modern C. – chqrlie Mar 31 '15 at 06:12
  • 1
    @chqrlie What ?? What?? – Gopi Mar 31 '15 at 06:29
  • 1
    You are mistaken on what the OP is asking. He knows `my_struct1` and `my_struct2` are different, He wonders why defining them in `foo.h` and redefining them in `foo.c` does not pose a problem when linking `foo.o` and `main.o`. This is a legitimate question. – chqrlie Mar 31 '15 at 06:34
  • OK, the magic in the linker is actually mandated by the C Standard. Still not very good style IMHO. – chqrlie Mar 31 '15 at 06:38
  • @chqrlie: no, the magic is _not_ mandated by the C standard; it is recognized as a common extension, but it is not mandated. Indeed, a strict reading of the standard says the code should compile to separate object files OK, but the two object files should not be linkable into a single program because the variables are doubly defined. – Jonathan Leffler Mar 31 '15 at 06:56
  • @Jonathan Leffler: Thank you for a precise reading of the Standard. My original comment holds, the code compiles and links thanks to linker magic. The OP is correct, relying on such behaviour is incorrect. – chqrlie Mar 31 '15 at 07:03