Determinant
Determinant

Reputation: 4036

C - In what circumstances does the external declaration become a definition?

From C99 standard 6.2.3:

If the declaration of an identifier of an object has file scope and no storage-class specifier, its linkage is external.

and 6.7

A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

— for an object, causes storage to be reserved for that object;
— for a function, includes the function body;99)
— for an enumeration constant or typedef name, is the (only) declaration of the identifier.

Unfortunately, I haven't found any further description on when the compiler shall regard the external declaration as a definition (which means the type must be complete and storage size is calculated).

So I did some experiments. First I noticed that:

struct A a;
int main() {
}

Is invalid, gcc says the type A is incomplete and it doesn't know how to allocate the storage for a. However, interestingly, we have the following valid code:

struct A a;
int main() {
}
struct A {int x;};

It's also reasonable since type A is completed at the end of the file. From two examples above, we can deduce that external declaration is checked at the end of file scope. (Still don't know where does the standard say about this)

However, array declaration is exceptional. The modified code is not longer valid:

struct A a[1];
int main() {
}
struct A {int x;};

And C99 standard does talk about this, it says elements of an array must be of completed type. So question comes about: is struct A a[1] a definition or a declaration? Don't be hasty to answer it. Check the following examples.

Here we have two files: a.c and b.c. In a.c:

#include <stdio.h>
int arr[10];
void a_arr_info() {
    printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}

while in b.c:

#include <stdio.h>
int arr[20];
void b_arr_info() {
    printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}
int main() {
    a_arr_info();
    b_arr_info();
}

The result is astonishing. The output shows that arr in both files refers to the same address. Which can be understood because arr are both in file scope, thus they're external linkage. The problem is, they have different size. In what file did the compiler take the declaration as definition and allocate the memory?

Why do I ask about this? Because, um, I'm working on a simplified C compiler project (course homework). So it might be important for me to figure it out. Although the homework does not go as far as this, I'm quite curious and would like to know more. Thanks!

Upvotes: 3

Views: 119

Answers (1)

Jens Gustedt
Jens Gustedt

Reputation: 78903

It is called 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.

So any compilation unit (.o file) that has such a tentative definition realizes the object. Linking two such units together has undefined behavior, you should usually encounter a "multiply defined symbol" error. Some compiler/linkers just do it, you have to ensure that such symbols have same size and type.

Upvotes: 2

Related Questions