External variable declaration and definition - c

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.

Related

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;

External, internal and no linkage or why this does not work?

According to C standard:
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. Within one translation unit, each declaration of an identifier with
internal
linkage
denotes the same object or function. Each declaration of an identifier with
no
linkage
denotes a unique entity.
In my example we have three separate declarations with each identifier having a different linkage.So why doesn't this work?
static int a; //a_Internal
int main(void) {
int a; //a_Local
{
extern int a; //a_External
}
return 0;
}
Error:
In function 'main':
Line 9: error: variable previously declared 'static' redeclared 'extern'
Why does compiler insist that I'm redeclaring instead of trying to access external object in another file?
Valid C++ example for reference:
static void f();
static int i = 0; // #1
void g() {
extern void f(); // internal linkage
int i; // #2 i has no linkage
{
extern void f(); // internal linkage
extern int i; // #3 external linkage
}
}
Both Clang and VC seem to be okay with my C example; only some versions of GCC (not all) produce the aforementioned error.
§6.2.2, 7 says:
If, within a translation unit, the same identifier appears with both
internal and external linkage, the behavior is undefined.
So, your program has undefined behaviour.
§6.2.2, 4 says that
extern int a; //a_External
has external linkage because the prior declaration visible in the scope int a; //a_Local has no linkage. But
static int a; //a_Internal
declares a with internal linkage. Hence, it's undefined per §6.2.2, 7.
The compiler is giving this error because inside the a_External scope, a_Internal is still accessible, thus you are redeclaring a_Internal from static to extern in a_External because of the name collision of a. This problem can be solved by using different variable names, for example:
static int a1; //a_Internal
int main(void) {
int a2; //a_Local
{
extern int a3; //a_External
}
return 0;
}
C standard says:
In the set of translation units each declaration of a particular
identifier with external linkage denotes the same entity (object or
function). Within one translation unit, each declaration of an
identifier with internal linkage denotes the same entity.
In the set of translation units we cannot have multiple distinct external entities with the same name, so the types of each declaration that denotes that single external entity should agree. We can check if types agree within one translation unit, this is done at compile-time. We cannot check if types agree between different translation units neither at compile-time nor at link-time.
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.
static int a; //a_Internal
int main(void) {
int a; //No linkage
{
extern int a; //a_External
}
return 0;
}
Here the previous declaration of identifier a has no linkage, so extern int a has external linkage. It means that we have to define int a in another translation unit. However GCC decided to reject this code with variable previously declared static redeclared 'extern' error, probably because we have undefined behavior according to C standard.

Can local and register variables be declared extern?

I have been wondering whether an extern can be declared locally and a register variable. If it can be what would be the restrictions imposed?
Local variables can be declared extern in some cases
Let's read the C99 N1256 standard draft.
The standard calls "local variables" as having "block scope".
6.7.1/5 "Storage-class specifiers" says:
The declaration of an identifier for a function that has block scope shall have no explicit storage-class specifier other than extern.
Then for what it means to add extern to a local variable, 6.2.2/4 "Linkages of identifiers" says:
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.
Lets break down those cases.
no prior declaration
void f() {
extern int i;
}
is the same as:
extern int i;
void f() {}
except that the declaration is only visible inside f.
This is because i has no prior declaration visible. So i has external linkage (the same linkage as global variables).
prior declaration specifies no linkage
int i;
void f() {
extern int i;
}
is the same as:
void f() {
extern int i;
}
because the prior declaration int i specifies no linkage because paragraph 6 says:
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.
prior declaration specifies internal or external linkage
extern int i;
void f() {
extern int i;
}
is the same as:
extern int i;
void f() {}
and:
static int i;
void f() {
extern int i;
}
is the same as:
static int i;
void f() {}
because in both cases we have a previous visible external and internal (static) linkage declarations respectively.
Initialize local extern
Invalid C:
void f() {
extern int i = 0;
}
because the block scope declaration has an initialization.
Valid C:
extern int i = 0;
void f() {}
but arguably bad style because equivalent to the shorter:
int i = 0;
void f() {}
because 6.7.8 Initialization says:
If the declaration of an identifier has block scope, and the identifier has external or internal linkage, the declaration shall have no initializer for the identifier.
Can local variables be declared extern?
No. But a global variable can be declared extern locally.
// file1.c
int Count;
// file2.c
void foo(void) {
extern int Count;
Count++;
}
Can register variables be declared extern?
No. A variable may not be extern and register.
C11 dr 6.7.1 Storage-class specifiers
1 storage-class-specifier:
typedef
extern
static
_Thread_local
auto
register
Constraints
2 At most, one storage-class specifier may be given in the declaration specifiers in a
declaration, except that _Thread_local may appear with static or extern)
6.9 External definitions of C99 states:
The storage-class specifiers auto and register shall not appear in the declaration
specifiers in an external declaration.
You only are allowed to define a global variable as extern. Telling the compiler (and linker) that it is defined elsewhere.
A local variable only exist in the local scope, as it is created on the stack or in a register. When the execution is not in the scope (anymore) the stack is unrolled (so free space becomes available again) or the register is used for other things, and the variable does not exist (anymore).
So defining a local extern would be 'weird' and impossible (due to the stack usage).
The phrase register variable is not clearly to me, so I would take one bold guess on what OP is really curious about, and rephrase the original question as: Could local variables be declared with extern specifier?, illustrated by the following snippet:
int main() {
extern int x; // Is this OK?
return 0;
}
The answer is yes.
scope (visibility) and storage are two independent and connected concept. Here, x is one local variable (scope), and it's only visible within this block. extern dictates the storage, meaning this is merely one declaration, this variable is defined somewhere else. Would recommend the C standard for definite reference.
As for the omitted register part, I assume OP meant one variable with register storage-class-specifier, like register int x. Then, it's illegal to specify register and extern at the same time.
int main() {
extern auto int x; // This is wrong.
return 0;
}
At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.
The symmetric question would be: is it valid to specify auto or register with global or external variables, and this is exactly what Alexey Frunze's answer is about.
auto int x; // This is wrong.
int main() {
return 0;
}

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.

Doubt related to extern keyword usage

AFAIK, extern keyword should be used for declaration and no value can be associated with the variable being declared with extern keyword. But supposing I write a statement like
extern int i = 10;
Should the compiler flag an error for the same? I have seen some compilers being tolerant and ignoring this? Why is this so? What does the 'C' standard says about this?
EDIT: #All, Thanks for the answers. I have a doubt still though. Suppose I have the definition for this variable without the extern linkage in another file say a.c and I add this statement in b.c. Still is it Ok for the compiler not to flag an error? Does it come under redefintion?
That's valid syntax, there is even an essentially identical example in the C99 standard. (See §6.9.2-4.)
It's true that the examples are not normative but I believe it was intended to be legal syntax. The compiler will often output a warning, because it doesn't really accomplish anything.
4 EXAMPLE 1
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
The following code ;
extern int i ;
declares a variable i, but does not instantiate it. If it is not also defined in the same compilation unit, the linker will attempt to resolve it from the object files and libraries that comprise the final executable.
However your example:
extern int i = 10 ;
initialises the object, and therefore must also instantiate it. In this case the extern keyword is redundant because the object is initialised in the same compilation unit (in fact the same statment). It is equivalent to:
extern int i ; // redundant
int i = 10 ;
Although in this last example the extern keyword is redundant, it is exactly equivalent to what you have when a global variable is declared in a header file, and instantiated in a source file that also includes that header (as it should, to allow the compiler to perform type checking).
You can test this as follows:
extern int i ;
int main()
{
i = 10 ;
}
The above will cause a linker error for unresolved variable i. Whereas:
extern int i = 10 ;
int main()
{
i = 10 ;
}
will link without problem.
The extern keyword indicates that the given variable is allocated in a different module. It has nothing to do with access to that variable. It's perfectly legal to assign to assign to an extern variable.
The purpose of extern keyword is to give the entity external linkage. Whether it is used in a declaration in a or definition makes no difference. There's absolutely no error in the code you posted.
If you prefer to think about it in terms of "export vs. import", then extern keyword applied to a non-defining declaration means that we are importing an entity defined in some other translation unit. When extern keyword applied to a definition, it means that we are exporting this entity to be used by other translation units. (Although it is worth noting that "export vs. import" is not exactly a standard way of thinking about the concept of C linkage.)
The reason you won't see the keyword used in definitions very often is because in C file-scope definitions have external linkage by default. So writing
extern int i = 10;
is valid, but redundant, since it is equivalent to plain
int i = 10;
Yet, from time to time in the actual code you might see people using this keyword with function declarations and definitions, even though it is superfluous there as well
extern void foo(int i); /* `extern` is superfluous */
...
extern void foo(int i) /* `extern` is superfluous */
{
/* whatever */
}

Resources