when is an object declaration not a definition? [duplicate] - c

C11 specifies in section 6.7 which declarations are also definitions:
A definition of an identifier is a declaration for that identifier that:
— for an object, causes storage to be reserved for that object;
[...]
I didn't find a comprehensive list of which object declarations cause storage to be reserved. Intuitively it is clear to me, but I wasn't able to get this information out of the C11 standard.

There's no definitive list because the standard just describes what are definitions and it's not in a single place. I'll try to sum it up here. I'll only use the type int here without any qualifiers (like const) for consistency.
If you add an initializer to a declaration, it's always a definition:
int x = 1;
Without an initializer, the following are definitions when they're in function scope:
int x;
auto int x; // auto is the default anyways
register int x; // register is just a hint, but would be "storage" as well
static int x; // also reserves storage, but with static duration
In file scope, the rules are a bit more complicated; the following are tentative definitions:
int x;
static int x;
The wording of the standard (§6.9.2p2) is:
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 this means that they eventually "become definitions" if they are not found to refer to another definition.
With the storage class extern and without an initializer, you don't have a definition:
extern int x; // <- not a definition
AFAIK, this should be the complete set of rules. Please feel free to edit/comment if I forgot something.

Related

Why does an explicit "extern" not allocate storage for an object?

I've been diving deeper into the C standard, and I'm confused about the way it talks about linkage and tentative definitions.
First, in this part of the standard it is stated that
extern (keyword) means static duration and external linkage (unless already declared internal)
static storage duration. The storage duration is the entire execution of the program, and the value
stored in the object is initialized only once, prior to main function. All objects declared static and
all objects with either internal or external linkage that aren't declared _Thread_local (since C11)
have this storage duration.
external linkage. The identifier can be referred to from any other translation units in the entire
program. All non-static functions, all extern variables (unless earlier declared static), and all file-
scope non-static variables have this linkage.
so far we have that variables declared in file scope have static storage duration and external linkage by default. Also, objects with static storage duration are initialized to zero, before the program starts.
But, after reading this part (tentative definitions) and this part (declarations) I can't find where it says that objects with an explicit "extern" keyword are not allocated storage.
Please be careful about the difference between the "extern" keyword itself and the term "external declarations".
"External declarations" are defined as
At the top level of a translation unit (that is, a source file with all the #includes after the preprocessor), every C program is a sequence of declarations, which declare functions and objects with external linkage. These declarations are known as external declarations because they appear outside of any function.
regardless of the presence or absence of an explicit "extern" keyword.
I suppose that my concrete question is where in the standard does it say that file scope objects, that have an implicit external linkage by default, are not allocated storage if they are declared with an explicit "extern".
I know this is the case because if one declares the same identifier in multiple translation units all but one must have "extern" so as not to get a redefinition error.
First, while cppreference.com has useful information it is not the C standard. The C11 standard can be found here.
This comes down to the difference between a declaration and a definition.
For an object, a declaration basically states that an object with a given type exists somewhere, while a definition is what actually allocates space for the object.
These terms are specified in sectin 6.7p5 of the C standard:
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;
for an enumeration constant, is the (only) declaration of the identifier;
for a typedef name, is the first (or only) declaration of the identifier.
By applying the extern keyword, if there is no initializer then this constitutes a declaration, and a declaration does not allocate storage for an object. Section 6.9.2p1-2 spells this out:
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.
A declaration with extern and no initializer does not fit the above definition of a tentative definition or an external definition.
Section 6.9.2p4 gives examples of declarations and definitions:
int i1 = 1; //definition, external linkage
static int i2 = 2; //definition, internal linkage
extern int i3 = 3; //definition, external linkage
int i4; //tentative definition, external linkage
static int i5; //tentative definition, internal linkage
int i1; //valid tentative definition, refers to previous
int i2; //6.2.2 renders undefined, linkage disagreement
int i3; //valid tentative definition, refers to previous
int i4; //valid tentative definition, refers to previous
int i5; //6.2.2 renders undefined, linkage disagreement
extern int i1; //refers to previous, whose linkage is external
extern int i2; //refers to previous, whose linkage is internal
extern int i3; //refers to previous, whose linkage is external
extern int i4; //refers to previous, whose linkage is external
extern int i5; //refers to previous, whose linkage is internal
In the C Standard (6.9.2 External object definitions ) there is written that
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.
So if you will write at file scope
extern int x = 1;
then this declaration with the storage-class specifier extern will be at the same time a definition of the object x.
Otherwise if an object is declared at file scope without an initializer but with the storage-class specifier extern then the compiler assumes that the object is defined in some other translation unit or in the same translation unit but somewhere else.
For example (here is declared a variable at file scope with internal linkage)
#include <stdio.h>
static int x = 10;
extern int x;
int main(void)
{
printf( "x = %d\n", x );
return 0;
}
If an object is declared at file scope without the storage-class specifier extern then the compiler generates a tentative definition.

Why do enumeration constants have no linkage?

I'm trying to understand linkage of enumeration constants and could not find a clear answer in the Standard N1570. 6.2.2(p6):
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.
So I need to understand that constants are not objects. Object is defined as 3.15:
region of data storage in the execution environment, the contents of
which can represent values
Also 6.2.2(p4) (emphasize mine):
For an identifier declared with the storage-class specifier extern in
a scope in which a prior declaration of that identifier is visible,31)
if the prior declaration specifies internal or external linkage, the
linkage of the identifier at the later declaration is the same as the
linkage specified at the prior declaration. If no prior declaration is
visible, or if the prior declaration specifies no linkage, then the
identifier has external linkage.
Anyway 6.4.4.3(p2):
An identifier declared as an enumeration constant has type int.
Combining all that I don't understand why
enum test {
a = 1
};
extern int a; //compile-error. UB?
does not compile? I expected a to have external linkage.
LIVE DEMO
Is the behavior well-defined? Can you provide a reference to the Standard explaining that?
An identifier declared as an enumeration constant has type int
that doesn't means it is a variable of type int
but
extern int a;
says there is a variable of type int named a, this is a conflict with the enumeration constant
Why does not enumeration constant have no linkage
for the same reason the constant 123 (also having type int, but whatever) has no linkage too
In 6.2.2 4, the standard intends to discuss linkage only for identifiers of objects and functions, but it fails to make this clear.
Enumeration constants are mere values, not objects or functions, and their identifiers never have any linkage.
Observe the declaration extern int a; declares a as an identifier for an int object. An int object is a different thing from an int value, so an enumeration constant named a cannot be the same thing as an int object named a. So the declaration of extern int a; is invalid even before linkage is considered.
Linkage does not matter here. In the same compilation unit you try to have two same identifiers Imagine if the code compiles:
enum test {
a = 1
};
extern int a;
int b = a; // which `a`? a as the external variable or `a` as a constant? How to decide.

Which object declarations in C cause storage to be reserved (i.e. are definitions)?

C11 specifies in section 6.7 which declarations are also definitions:
A definition of an identifier is a declaration for that identifier that:
— for an object, causes storage to be reserved for that object;
[...]
I didn't find a comprehensive list of which object declarations cause storage to be reserved. Intuitively it is clear to me, but I wasn't able to get this information out of the C11 standard.
There's no definitive list because the standard just describes what are definitions and it's not in a single place. I'll try to sum it up here. I'll only use the type int here without any qualifiers (like const) for consistency.
If you add an initializer to a declaration, it's always a definition:
int x = 1;
Without an initializer, the following are definitions when they're in function scope:
int x;
auto int x; // auto is the default anyways
register int x; // register is just a hint, but would be "storage" as well
static int x; // also reserves storage, but with static duration
In file scope, the rules are a bit more complicated; the following are tentative definitions:
int x;
static int x;
The wording of the standard (§6.9.2p2) is:
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 this means that they eventually "become definitions" if they are not found to refer to another definition.
With the storage class extern and without an initializer, you don't have a definition:
extern int x; // <- not a definition
AFAIK, this should be the complete set of rules. Please feel free to edit/comment if I forgot something.

What will be the value stored in a variable when it is initialised with extern keyword when it is already declared as a global variable?

When we declare a global variable, it is initialised to its default value. But when we initialise a variable using the extern keyword, why does the variable retain the value with which it is initialised using the extern keyword?
For instance, in the code below why is the output 9 and not a compile time error? Since there is no external linkage of variable x from any other source file, so x has two copies and we are initialising the variable twice so this should be an error. Please clarify this; I am a bit confused in the flow of this code.
#include <stdio.h>
extern int x=9;
int x;
int main()
{
printf("%d",x);
return 0;
}
extern int x = 9; means the same as int x = 9;. The extern keyword has no effect for a definition that already has external linkage and an initializer.
int x; is called a tentative definition.
This is described well by C11 6.9.2/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.
This translation unit does contain an external definition for x, so the tentative definition has no effect. It doesn't matter whether the external definition is before or after the tentative definition.
"external definition" means a non-tentative definition at file scope -- not to be confused with extern or "external linkage", although in your example x does happen to have external linkage.
So your code is exactly the same as:
int x = 9;

Why is the storage class of global variables in C implicitly defined as "extern"?

When we declare any global variable, for instance
int x;
it is equivalent to
extern int x;
Now by default global variables are initialized to 0 by the compiler, which means they are allocated memory. But if I simply write
extern int x;
then this will only declare the variable, while no memory would be allocated to it. So, my query is that if I write extern before int x or I do not write it, in case of global variables, how is the compiler treating them differently? In the case where I simply write int x, it allocates memory and simultaneously it puts extern before int x, while in the case where I write extern int x, it only declares the variable while no memory is allocated to it. Please clarify how the compiler is behaving in both ways.
The very premise of your question is incorrect. This
int x;
is a tentative definition (which will turn into a normal definition of x by the end of the translation unit).
This
extern int x;
is a non-defining declaration, which is not a definition at all.
They are not even remotely equivalent.
A loose equivalent of your original definition would be
extern int x = 0;
This is a definition. But this is not an exact equivalent, since this definition is not tentative.
Keyword extern turns an object definition into a non-defining declaration if (and only if), there is no explicit initializer. If an explicit initializer is present, a definition remains a definition, even if you add extern to it.
This can be answered by understanding external object definition and Tentative definition.
Quoting C11, chapter §6.9.2, (emphasis mine)
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.

Resources