Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between static and extern in C?

Tags:

c

What is the difference between static and extern in C?

like image 247
Sambhav jain Avatar asked Sep 10 '10 12:09

Sambhav jain


People also ask

Can we use static and extern together in C?

3.1. Static variables in C have the following two properties: They cannot be accessed from any other file. Thus, prefixes “ extern ” and “ static ” cannot be used in the same declaration. They maintain their value throughout the execution of the program independently of the scope in which they are defined.

What is auto static and extern in C?

auto is used for a local variable defined within a block or function. register is used to store the variable in CPU registers rather memory location for quick access. Static is used for both global and local variables. Each one has its use case within a C program. Extern is used for data sharing between C project files ...

Why do we use extern in C?

“extern” keyword is used to extend the visibility of function or variable. By default the functions are visible throughout the program, there is no need to declare or define extern functions. It just increase the redundancy. Variables with “extern” keyword are only declared not defined.

What are intern static and extern static variables in C?

Internal static variables has persistent storage with block scope(works only within a particular block or function). External static variables has permanent storage with file scope(works throughout the program).


3 Answers

From http://wiki.answers.com/Q/What_is_the_difference_between_static_and_extern:

The static storage class is used to declare an identifier that is a local variable either to a function or a file and that exists and retains its value after control passes from where it was declared. This storage class has a duration that is permanent. A variable declared of this class retains its value from one call of the function to the next. The scope is local. A variable is known only by the function it is declared within or if declared globally in a file, it is known or seen only by the functions within that file. This storage class guarantees that declaration of the variable also initializes the variable to zero or all bits off.

The extern storage class is used to declare a global variable that will be known to the functions in a file and capable of being known to all functions in a program. This storage class has a duration that is permanent. Any variable of this class retains its value until changed by another assignment. The scope is global. A variable can be known or seen by all functions within a program.

like image 138
Danail Avatar answered Oct 08 '22 00:10

Danail


static means a variable will be globally known only in this file. extern means a global variable defined in another file will also be known in this file, and is also used for accessing functions defined in other files.

A local variable defined in a function can also be declared as static. This causes the same behaviour as if it was defined as a global variable, but is only visible inside the function. This means you get a local variable whose storage is permanent and thus retain its value between calls to that function.

I'm no C expert so I might be wrong about this, but that's how I've understood static and extern. Hopefully someone more knowledgable will be able to provide you with a better answer.

EDIT: Corrected answer according to comment provided by JeremyP.

like image 24
gablin Avatar answered Oct 08 '22 01:10

gablin


You can apply static to both variables and functions. There are two answers that discuss the behaviour of static and extern with respect to variables, but neither really covers functions. This is an attempt to rectify that deficiency.

TL;DR

  • Use static functions whenever possible.
  • Only declare external functions in headers.
  • Use the headers where the functions are defined and where the functions are used.
  • Don't declare functions inside other functions.
  • Don't exploit the GCC extension with function definitions nested inside other functions.

External functions

By default, functions in C are visible outside the translation unit (TU — basically the C source file and included headers) in which they are defined. Such functions can be called by name from any code that notifies the compiler that the function exists — usually by a declaration in a header.

For example, the header <stdio.h> makes visible declarations of functions such as printf(), fprintf(), scanf(), fscanf(), fopen(), fclose(), and so on. If a source file includes the header, it can call the functions. When the program is linked, the correct library must be specified to satisfy the function definition. Fortunately, the C compiler automatically provides the library that provides (most of) the functions in the standard C library (and it usually provides a lot more functions than just those). The 'most of' caveat applies because on many systems (Linux, for instance, but not macOS), if you use functions declared in the <math.h> header, you need to link with the maths library ('math' library if you're American), which usually is indicated by the option -lm on the linker command line.

Note that external functions should be declared in headers. Each external function should be declared in one header, but one header may declare many functions. The header should be used both in the TU where each function is defined and in each TU that uses the function. You should never need to write a declaration for a global function in a source file (as opposed to a header file) — there should be a header to declare the function and you should use that header to declare it.

Static functions

As an alternative to generally visible functions, you can make your own functions static. This means that the function cannot be called by name from outside the TU in which it is defined. It is a hidden function.

The primary advantage of static functions is hiding details which the outside world doesn't need to know about. It is a basic but powerful information hiding technique. You also know that if a function is static, you do not need to look for uses of the function outside the current TU, which can greatly simplify the search. However, if the functions are static, there can be multiple TUs which each contain a definition of a function with the same name — each TU has its own function, which may or may not do the same thing as a function with the same name in a different TU.

In my code, I qualify all functions except main() with the keyword static by default — unless there's a header that declares the function. If I subsequently need to use the function from elsewhere, it can be added to the appropriate header and the keyword static removed from its definition.

Declaring functions inside other functions

It is possible, but very inadvisable, to declare a function inside the scope of another function. Such declarations fly in the face of Agile Development maxims such as SPOT (Single Point of Truth) and DRY (Don't Repeat Yourself). They're also a maintenance liability.

However, you can, if you so desire, write code such as:

extern int processor(int x);

int processor(int x)
{
    extern int subprocess(int);
    int sum = 0;
    for (int i = 0; i < x; i++)
        sum += subprocess((x + 3) % 7);
    return sum;
}

extern int subprocess(int y);

int subprocess(int y)
{
    return (y * 13) % 37;
}

The declaration in processor() suffices for it to use subprocess(), but is otherwise unsatisfactory. The extern declaration before the definition is necessary if you use GCC compiler options such as:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
>     -c process.c
process.c:12:5: error: no previous prototype for ‘subprocess’ [-Werror=missing-prototypes]
 int subprocess(int y)
     ^~~~~~~~~~
cc1: all warnings being treated as errors
$

This is, I find, a good discipline, similar to what C++ enforces. It's another reason I make most functions static, and define the functions before they're used. The alternative is to declare static functions at the top of the file and then define them in whatever order seems appropriate. There are some merits to both techniques; I prefer to avoid the need to declare and define the same function in the file by defining before use.

Note that you cannot declare a static function within another function, and if you attempt to define a function such as subprocess() as a static function, the compiler gives an error:

process.c:12:16: error: static declaration of ‘subprocess’ follows non-static declaration
     static int subprocess(int y)
                ^~~~~~~~~~
process.c:5:20: note: previous declaration of ‘subprocess’ was here
         extern int subprocess(int);
                    ^~~~~~~~~~

Since functions that are externally visible should be declared in a header, there is no need to declare them inside a function, so you should never run into this as a problem.

Again, the extern is not necessary in the function declaration inside the function; if omitted, it is assumed. This can lead to unexpected behaviour in novice programs here on SO — you sometimes find a function declaration where a call was intended.

With GCC, the option -Wnested-externs identifies nested extern declarations.

Called by name vs called by pointer

If you have a nervous disposition, stop reading now. This gets hairy!

The 'called by name' comment means that if you have a declaration such as:

extern int function(void);

you can write in your code:

int i = function();

and the compiler and linker will sort things out so that the function is called and the result used. The extern in the declaration of the function is optional but explicit. I normally use it in a header file to match the declaration of those rare global variables — where the extern is not optional but mandatory. Many people disagree with me on this; do as you wish (or must).

Now what about static functions? Suppose the TU reveal.c defines a function static void hidden_function(int) { … }. Then, in another TU openness.c, you cannot write :

hidden_function(i);

Only the TU that defines the hidden function can use it directly. However, if there's a function in reveal.c that returns a function pointer to the hidden_function(), then the code openness.c can call that other function (by name) to get a pointer to the hidden function.

reveal1.h

extern void (*(revealer(void)))(int);

Obviously, that's a function that takes no arguments and returns a pointer to a function that takes an int argument and returns no value. No; it isn't pretty. One of the times it makes sense to use typedef on pointers is with pointers to functions (reveal2.h):

typedef void (*HiddenFunctionType)(int);
extern HiddenFunctionType revealer(void);

There: much simpler to understand.

See Is it a good idea to typedef pointers for a general discussion on the subject of typedef and pointers; the short summary is "it isn't a good idea except perhaps with function pointers".

reveal1.c

#include <stdio.h>
#include "reveal1.h"

static void hidden_function(int x)
{
    printf("%s:%s(): %d\n", __FILE__, __func__, x);
}

extern void (*(revealer(void)))(int)
{
    return hidden_function;
}

Yes, it is legitimate (but very unusual) to define the function with an explicit extern — I very, very seldom do it, but here it emphasizes the role of extern and contrasts it with static. The hidden_function() can be returned by revealer(), and could be called by code inside reveal.c. You can remove the extern without changing the meaning of the program.

openness1.c

#include <stdio.h>
#include "reveal1.h"

int main(void)
{
    void (*revelation)(int) = revealer();
    printf("%s:%s: %d\n", __FILE__, __func__, __LINE__);
    (*revelation)(37);
    return 0;
}

This file cannot usefully contain a direct call by name to hidden_function() because it is hidden in the other TU. However, the revealer() function declared in reveal.h can be called by name and it returns a pointer to the hidden function, which can then be used.

reveal2.c

#include <stdio.h>
#include "reveal2.h"

static void hidden_function(int x)
{
    printf("%s:%s(): %d\n", __FILE__, __func__, x);
}

extern HiddenFunctionType revealer(void)
{
    return hidden_function;
}

openness2.c

#include <stdio.h>
#include "reveal2.h"

int main(void)
{
    HiddenFunctionType revelation = revealer();
    printf("%s:%s: %d\n", __FILE__, __func__, __LINE__);
    (*revelation)(37);
    return 0;
}

Sample outputs

Not the most exciting output in the world!

$ openness1
openness1.c:main: 7
reveal1.c:hidden_function(): 37
$ openness2
openness2.c:main: 7
reveal2.c:hidden_function(): 37
$
like image 17
Jonathan Leffler Avatar answered Oct 07 '22 23:10

Jonathan Leffler