Benefits of using extern declaration in same file as definition? - c

I'm reading the source of a C project and am trying to discern why a particular use of extern is being used. (Note: This question is related but for C++, not C.) Say I have four header files that each declare an extern const struct:
extern const struct puppy fido;
This struct is defined in a source file like so:
extern const struct puppy fido;
static const struct puppy *puppies[] = { &fido };
const struct puppy fido = { "fido", 10, 10 };
Is there some kind of benefit to marking the variable declaration as extern in the source file, when those variables are defined in that very same source file?

The only advantage would be that any code between the declaration and the definition will be able to access the variable. Apart from that, the extern declaration in the same file is pointless.

Lundin is correct, that in this case extern does not affect visibility of fido in other source files. In your example code, it is used as a forward declaration.
Relevant part from C99 standard, section 6.2.2:
4) For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, 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.
5) If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier,
its linkage is external
In C++, behavior would be different, because variables declared with const are implicitly static. In C++, you are required to declare the variable with extern the first time, or other compilation units would not find it.
C++98 standard, section 3.5.3:
A name having namespace scope (3.3.5) has internal linkage if it is the name of
— an object, reference, function or function template that is explicitly declared static or,
— an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage;

Sometimes, when you write a source file, you just want a piece of a header file, not the whole thing.
Repeating the extern declaration in the source file allows you not to include the header file in that specific source file.
This is not necessarily a good practice.

extern const struct puppy fido;
// ...
const struct puppy fido = { "fido", 10, 10 };
If const struct puppy fido = { "fido", 10, 10 }; is compiled in 2 different .c files, it will fail. There should be #ifndef and endif before and after the const struct puppy fido = { "fido", 10, 10 }; in order to include it once.
A good practice (globals variables have to be avoided in general) is to declare with extern in the .h files, and to put the definition of the variable in a .c file.

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.

Multiple declaration of same struct variable ok?

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.
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.
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.

External variable declaration and definition

a)The definition of an external variable is same as that of a local variable,ie, int i=2; (only outside all functions).
But why is extern int i=2; too working as the definition? Isn't extern used only in variable declaration in other files?
b)file 1
#include<stdio.h>
int i=3;
int main()
{
printf("%d",i);
fn();
}
file2
int i; // although the declaration should be: extern int i; So, why is this working?
void fn()
{
printf("%d",i);
}
OUTPUT: 3 in both cases
For historical reasons, the rules for determination of linkage and when a declaration provides a definition are a bit of a mess.
For your particular example, at file scope
extern int i = 2;
and
int i = 2;
are equivalent external definitions, ie extern is optional if you provide an initializer.
However, if you do not provide an initializer, extern is not optional:
int i;
is a tentative definition with external linkage, which becomes an external definition equivalent to
int i = 0;
if the translation unit doesn't contain another definition with explicit initializer.
This is different from
extern int i;
which is never a definition. If there already is another declaration of the same identifier visible, then the variable will get its linkage from that; if this is the first declaration, the variable will have external linkage.
This means that in your second example, both file1 and file2 provide an external definition of i, which is undefined behaviour, and the linker is free to choose the definition it likes best (it may also try to make demons fly out of your nose). There's a common extension to C (see C99 Annex J.5.11 and this question) which makes this particular case well-defined.
In C, an extern with an initialization results in a variable being allocated. That is the declaration will be considered a defining declaration. This is in contrast with the more common use of extern.
The C standard says:
6.9.2 External object definitions
.....
If the declaration of an identifier for an object has file scope and an initializer, the
declaration is an external definition for the identifier.
As for the second part of your question, your declaration int i at file scope has external linkage. If you want to give it internal linkage you need to declare it static int i. The C standard says:
6.2.2 Linkages of identifiers
......
If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

What is the use of declaring a static variable as extern inside a function?

#include <stdio.h>
static i = 5;
int main()
{
extern int i;
printf("%d\n",i);
return 0;
}
Can someone give any use-case for declaring a static variable as extern inside a function block?
NEW:
Why is this not allowed?
int main()
{
static i = 5;
extern int i;
printf("%d\n",i);
return 0;
}
this is useful when you need to access a variable that resides within another translation unit, without exposing the external variable globally (for a few reasons, like name collision, or that the the variable shouldn't be directly accessed, so static was used to limit its scope, but that TU's header still needs access).
As an example, lets say we have a translation unit foo.c, it contains:
//foo.c
static int i = 0;
i shouldn't be changed or directly accessed outside foo.c, however, foo.h comes along requiring access to i for an inline function, but i shouldn't be exposed to any translation unit using foo.h, so we can use extern at functional level, to expose it only during the scope of IncI, the inline function requiring the use of i:
//foo.h
inline void IncI(int val)
{
extern int i;
i += val;
}
Your second example is 'disallowed' because the compiler thinks you are trying to bind two different variables to the same symbol name, ie: it creates the static i at local scope, but searches for the extern int i at global scope, but doesn't find it, because static i as at the function scope. a more clever compiler would just fix the linkage to the static i, whether or not this follows standards I wouldn't know.
Now that I have a C standards document to work from (shame on me I know...), we can see what the official stance is (in C99):
6.2.2 Linkages of identifiers
Section 3:
If the declaration of a file scope identifier for an object or a function contains the storageclass
specifier static, the identifier has internal linkage.
Section 4:
For an identifier declared with the storage-class specifier extern in
a scope in which a prior declaration of that identifier is visible,
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.
thus, because static will cause internal linkage, the extern will bring that linkage into the current scope. there is also a footnote stating that this may cause hiding of variables:
23) As specified in 6.2.1, the later declaration might hide the prior declaration.
In your second case, you are telling the compiler you have a static (local) variable i and another (global) variable i that is defined somewhere else.

extern keyword with function names

I know that static keyword makes a C function/variable is file-scoped.
And I've read that If I want to make a variable global scope (accessed by more than one file), I should do:
in the .c file:
int my_global_var;
// main()....
in the .h file:
extern int my_global_var;
So, any one will include my .h file will be able to reference my_global_var which is already externed.
And I read also this is required for functions as well but I am using gcc 4.x and I don't extern the function in the .h file and other programs can successfully link it.
So, the question is...
Is the behavior of the non-static function linkage is the default or should I extern non-static functions to adhere to the standard??
From the standard, 6.2.2
5 If the declaration of an identifier for a function has no storage-class
specifier, its linkage is determined
exactly as if it were declared with
the storage-class specifier extern. If
the declaration of an identifier for
an object has file scope and no
storage-class specifier, its linkage
is external.
Meaning, it's extern by default.
Both function and object declarations are extern by default. However, you need to add an explicit extern to your object declarations in header files to avoid a re-definition: without a storage-class specifier, any file-scoped object declaration will actually be something called a tentative definition and reserve storage for the object within the current translation-unit.
For consistency, I unnecessarily use extern even for function declarations. In most cases, I declare objects within headers as one of
extern int foo;
static const int bar = 42;
and functions as one of
extern int spam(void);
static inline int eggs(void) { return 42; }

Resources