Thursday, December 22, 2011

extern vs global

Describe the use and technicalities related to extern in C

Extern Concepts
Basically extern keyword extends the visibility of the C variables and C functions.External variables have global scope across the entire program (provided extern declarations are used in files other than where the variable is defined), and a lifetime over the the entire program run.

The extern storage class specifier lets you declare objects that several source files can use. An extern declaration makes the described variable usable by the succeeding part of the current source file. This declaration does not replace the definition. The declaration is used to describe the variable that is externally defined.
An extern declaration can appear outside a function or at the beginning of a block. If the declaration describes a function or appears outside a function and describes an object with external linkage, the keyword extern is optional.
If a declaration for an identifier already exists at file scope, any extern declaration of the same identifier found within a block refers to that same object. If no other declaration for the identifier exists at file scope, the identifier has external linkage.

Storage duration of external variables

All extern objects have static storage duration. Memory is allocated for extern objects before the main function begins running, and is freed when the program terminates. The scope of the variable depends on the location of the declaration in the program text. If the declaration appears within a block, the variable has block scope; otherwise, it has file scope.

Linkage of external variables:

Like the scope, the linkage of a variable declared extern depends on the placement of the declaration in the program text. If the variable declaration appears outside of any function definition and has been declared static earlier in the file, the variable has internal linkage; otherwise, it has external linkage in most cases. All object declarations that occur outside a function and that do not contain a storage class specifier declare identifiers with external linkage.

Initialization of external variables

You can initialize any object with the extern storage class specifier at global scope in C or at namespace scope in C++. The initializer for an extern object must either:
  • Appear as part of the definition and the initial value must be described by a constant expression; or
  • Reduce to the address of a previously declared object with static storage duration. You may modify this object with pointer arithmetic. (In other words, you may modify the object by adding or subtracting an integral constant expression.)
If you do not explicitly initialize an extern variable, its initial value is zero of the appropriate type. Initialization of an extern object is completed by the time the program starts running.


C Functions
Let us first take the easy case. Use of extern with C functions. By default, the declaration and definition of a C function have “extern” prepended with them. It means even though we don’t use extern with the declaration/definition of C functions, it is present there. For example, when we write.

int foo(int arg1, char arg2);
There’s an extern present in the beginning which is hidden and the compiler treats it as below.
extern int foo(int arg1, char arg2);
Same is the case with the definition of a C function (Definition of a C function means writing the body of the function). Therefore whenever we define a C function, an extern is present there in the beginning of the function definition. Since the declaration can be done any number of times and definition can be done only once, we can notice that declaration of a function can be added in several C/H files or in a single C/H file several times. But we notice the actual definition of the function only once (i.e. in one file only). And as the extern extends the visibility to the whole program, the functions can be used (called) anywhere in any of the files of the whole program provided the declaration of the function is known. (By knowing the declaration of the function, C compiler knows that the definition of the function exists and it goes ahead to compile the program). So that’s all about extern with C functions.
C Variables:
How would we declare a C variable without defining it? The answer goes as follows.
extern int var;
Here, an integer type variable called var has been declared (remember no definition i.e. no memory allocation for var so far). And we can do this declaration as many times as needed. (remember that declaration can be done any number of times) So far so good.
Now how would you define a variable.The answer is as follows.
int var;
Here, an integer type variable called var has been declared as well as defined. (remember that definition is the super set of declaration). Here the memory for var is also allocated.

Now here comes the surprise, when we declared/defined a C function, we saw that an extern was present by default. While defining a function, we can prepend it with extern without any issues. But it is not the case with C variables. If we put the presence of extern in variable as default then the memory for them will not be allocated ever, they will be declared only. Therefore, we put extern explicitly for C variables when we want to declare them without defining them. Also, as the extern extends the visibility to the whole program, by externing a variable we can use the variables anywhere in the program provided we know the declaration of them and the variable is defined somewhere.


Now let us try to understand extern with examples.
Example 1:
int var;
int main(void)
{
   var = 10;
   return 0;
}
Analysis: This program is compiled successfully. Here var is defined (and declared implicitly) globally.
Example 2:
extern int var;
int main(void)
{
  return 0;
}
Analysis: This program is compiled successfully. Here var is declared only. Notice var is never used so no problems.
Example 3:
extern int var;
int main(void)
{
 var = 10;
 return 0;
}
Analysis: This program throws error in compilation. Because var is declared but not defined anywhere. Essentially, the var isn’t allocated any memory. And the program is trying to change the value to 10 of a variable that doesn’t exist at all.Note that it is possible to declare an extern variable inside a function, but it can only be defined outside of a function.
i.e.The below also throws error
int main(){
       extern int i=10;
        getchar();
        return 0;
} BUT again the code below works perfectly...
extern int i=10;
int main(){
        i++;
        getchar();
        return 0;
}
Example 4:
#include "somefile.h"
extern int var;
int main(void)
{
 var = 10;
 return 0;
}
Analysis: Supposing that somefile.h has the definition of var. This program will be compiled successfully.
Example 5:
extern int var = 0;
int main(void)
{
 var = 10;
 return 0;
}
Analysis: Guess this program will work? Well, here comes another surprise from C standards. They say that..if a variable is only declared and an initializer is also provided with that declaration, then the memory for that variable will be allocated i.e. that variable will be considered as defined. Therefore, as per the C standard, this program will compile successfully and work.
How Extern functions are used

add.cpp

int add(int x, int y)
{
    return x + y;
}
main.cpp
#include <iostream>
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
The above program dosnt compile as When the compiler is compiling a code file, it does not know about the existence of functions that live in any other files. This is done so that files may have functions or variables that have the same names as those in other files without causing a conflict.However, in this case, we want main.cpp to know about (and use) the add() function that lives in add.cpp. To give main.cpp access to the add function, we can use a forward declaration as follows,
#include <iostream>
int add(int x, int y); // forward declaration using function prototyp
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
However, as programs grow larger and larger, it becomes tedious to have to forward declare every function you use that lives in a different file. To solve that problem, the concept of header files was introduced.
Note:In the above case we are not required to declare add() as extern as functions are by default extern in C
How Extern variables are used

1.By declaring extern in source files

Extern variable in C is an extension of global variable concept. First, let us see what is a global variable and the difference between a global variable and an extern variable.

What is a global variable?

       Any variable declared outside a function block is a global variable. A global variable  can be accessed by any function in the file in which it is defined. The scope of the global variable is throughout the file in which it is present. 
int globalVar;
   The variable globalVar is defined as a global variable for which memory space is allocated and the memory location is accessed by the name globalVar. Since there is no initial value specified, the variable gets initialized to zero. This variable can now be accessed from any function in the file in which this definition is present.

What is an extern  variable?

   Assume a scenario where you would like to access the global variable globalVar in another program. If you happen to declare a global variable with the same name in the second file, the compiler will not allow since no two variables can be declared with the same name.  So, how would you access the global variable globalVar defined in the first file? Simple, the answer to this is extern.
extern int globalVar;
  When you use extern keyword before the global variable declaration, the compiler understands you want to access a variable being  defined in another program or file, and hence not to allocate any memory for this one. Instead, it simply points to the global variable defined in the other file. In this fashion, you can use the extern keyword in any number of programs to access the global variable globalVar. However, the definition should only be at one place.

Let us see an example:

# cat f1.c
#include <stdio.h>

int globalVar=3;

void fun();

int main()
{
 fun();
 printf("Global var in f1 is %d\n", globalVar);
 return 1;
}
     The above file f1.c contains the main program in which a function fun is being called. The main point here is the definition of the variable globalVar. At this juncture, the globalVar is simply a global variable.

#cat f2.c
#include <stdio.h>

extern int globalVar;
void fun()
{
  printf("Global var in f2 is %d\n", globalVar);
  globalVar++;
}
     In the above file f2.c, the function fun wants to access the variable globalVar being defined in the file f1.c. In order to access the variable, the extern keyword is used for declaration of the globalVar variable and hence no memory is allocated for globalVar, instead it starts pointing to the globalVar in the f1.c .

2.By declaring extern in header file and including the header
Although there are other ways of doing it, the clean, reliable way to declare and define global variables is to use a header file file3.h to contain an extern declaration of the variable. The header is included by the one source file that defines the variable and by all the source files that reference the variable. For each program, one source file (and only one source file) defines the variable. Similarly, one header file (and only one header file) should declare the variable.

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
/* Variable defined here */
int global_variable = 37;    /* Definition checked against declaration */
int increment(void) { return global_variable++; }

file2.c

#include "file3.h"
#include <stdio.h>
void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}
That's the best way to use them.

Global Variables vs Extern variables:
Global variables and extern variables are very similar, but with a major difference. Let's say you have a global variable in your header file, like this: 
( Refer how u can add the variable declaration in the source code as extern, i think we cant add into header files as it said) 
int x = 0; 

Now, in each file that includes that header file, there will be a new variable called x. Modifying x in file1.cpp won't modify x in file2.cpp. 

Now, for externs... Say you have something like this in your header: 
extern int x; 
Then, in file1.cpp (which includes your header): 
int x = 0; 
Then, in file2.cpp (also includes the header): 
x = 46; 
x in file1.cpp is also now equal to 46. You see, when you use an extern variable, you're telling the compiler that you want to use that variable across all of your source files. If you use a regular global, however, you create a new variable in each of your source files. 




Useful Links:

1 comment: