Reputation: 272
I have an array of typedef struct Books Book
in a file1.h declared as Book books[20]
. Now, I initialise the array in file1.c and then use it in file2.c (that includes file1.h), without defining it. The program works fine. But I don't understand why. Since I have not used extern with books, shouldn't I be getting a compile-time error (undefined reference) for file2.c?
file1.h
Book books[20];
file1.c:
#include file1.h
void intializeBooks(){
/**
*Code to initialize book name and its cost
*/
}
file2.c:
#include "file1.h"
void addOrder() {
for (int i = 0; i < 20; i++) {
if (books[i].cost != -999) {
fprintf(fptr, "\t\t%d.%s\n", (i + 1),
books[i].name); //works correctly
} else
break;
}
Upvotes: 1
Views: 94
Reputation: 60058
A global declaration without an initializer constitutes a "tentative definition":
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.
Basically, tentative definitions serve as declarations that become a definition unless it is overridden by an actual definition.
The standard guarantees that this'll work in just one translation unit: i.e., what you are doing with the Books
variable being tentative in several translation unit is not guaranteed to work. However, practically it does work since common linkers (upcoming pun intended) use a special symbol type called a "common symbol" for symbols that were still tentative at the end of a translation unit, and with that special symbol type. tentative definitions work even across several translation units. (If there's one non-tentative definition, it overrides all the other ones; otherwise the tentative definitions get merged into just one zero-initialized external definition.)
Long story short, if you want to be perfectly standard compliant, you should replace the tentative definition in the header with a proper extern
declaration. If you don't, your code might still work. (On Unix-based platforms, it probably will work because common symbols are a feature that's very old, even though it's nonstandard feature.) Either way it's bad practice to put tentative definitions in header files.
Upvotes: 2