Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Effects of the extern keyword on C functions

People also ask

What does the extern keyword do 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.

How do extern functions work?

the extern keyword is used to extend the visibility of variables/functions. Since functions are visible throughout the program by default, the use of extern is not needed in function declarations or definitions. Its use is implicit. When extern is used with a variable, it's only declared, not defined.

What is the difference between extern in function and in function?

Explanation: extern int fun(); declaration in C is to indicate the existence of a global function and it is defined externally to the current module or in another file. int fun(); declaration in C is to indicate the existence of a function inside the current module or in the same file.

Is extern keyword necessary?

If the declaration describes a function or appears outside a function and describes an object with external linkage, the keyword extern is optional. If you do not specify a storage class specifier, the function is assumed to have external linkage.


We have two files, foo.c and bar.c.

Here is foo.c

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

Now, here is bar.c

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   if (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

As you can see, we have no shared header between foo.c and bar.c , however bar.c needs something declared in foo.c when it's linked, and foo.c needs a function from bar.c when it's linked.

By using 'extern', you are telling the compiler that whatever follows it will be found (non-static) at link time; don't reserve anything for it in the current pass since it will be encountered later. Functions and variables are treated equally in this regard.

It's very useful if you need to share some global between modules and don't want to put / initialize it in a header.

Technically, every function in a library public header is 'extern', however labeling them as such has very little to no benefit, depending on the compiler. Most compilers can figure that out on their own. As you see, those functions are actually defined somewhere else.

In the above example, main() would print hello world only once, but continue to enter bar_function(). Also note, bar_function() is not going to return in this example (since it's just a simple example). Just imagine stop_now being modified when a signal is serviced (hence, volatile) if this doesn't seem practical enough.

Externs are very useful for things like signal handlers, a mutex that you don't want to put in a header or structure, etc. Most compilers will optimize to ensure that they don't reserve any memory for external objects, since they know they'll be reserving it in the module where the object is defined. However, again, there's little point in specifying it with modern compilers when prototyping public functions.

Hope that helps :)


As far as I remember the standard, all function declarations are considered as "extern" by default, so there is no need to specify it explicitly.

That doesn't make this keyword useless since it can also be used with variables (and it that case - it's the only solution to solve linkage problems). But with the functions - yes, it's optional.


You need to distinguish between two separate concepts: function definition and symbol declaration. "extern" is a linkage modifier, a hint to the compiler about where the symbol referred to afterwards is defined (the hint is, "not here").

If I write

extern int i;

in file scope (outside a function block) in a C file, then you're saying "the variable may be defined elsewhere".

extern int f() {return 0;}

is both a declaration of the function f and a definition of the function f. The definition in this case over-rides the extern.

extern int f();
int f() {return 0;}

is first a declaration, followed by the definition.

Use of extern is wrong if you want to declare and simultaneously define a file scope variable. For example,

extern int i = 4;

will give an error or warning, depending on the compiler.

Usage of extern is useful if you explicitly want to avoid definition of a variable.

Let me explain:

Let's say the file a.c contains:

#include "a.h"

int i = 2;

int f() { i++; return i;}

The file a.h includes:

extern int i;
int f(void);

and the file b.c contains:

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

int main(void){
    printf("%d\n", f());
    return 0;
}

The extern in the header is useful, because it tells the compiler during the link phase, "this is a declaration, and not a definition". If I remove the line in a.c which defines i, allocates space for it and assigns a value to it, the program should fail to compile with an undefined reference. This tells the developer that he has referred to a variable, but hasn't yet defined it. If on the other hand, I omit the "extern" keyword, and remove the int i = 2 line, the program still compiles - i will be defined with a default value of 0.

File scope variables are implicitly defined with a default value of 0 or NULL if you do not explicitly assign a value to them - unlike block-scope variables that you declare at the top of a function. The extern keyword avoids this implicit definition, and thus helps avoid mistakes.

For functions, in function declarations, the keyword is indeed redundant. Function declarations do not have an implicit definition.


The extern keyword takes on different forms depending on the environment. If a declaration is available, the extern keyword takes the linkage as that specified earlier in the translation unit. In the absence of any such declaration, extern specifies external linkage.

static int g();
extern int g(); /* g has internal linkage */

extern int j(); /* j has tentative external linkage */

extern int h();
static int h(); /* error */

Here are the relevant paragraphs from the C99 draft (n1256):

6.2.2 Linkages of identifiers

[...]

4 For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,23) 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.


Inline functions have special rules about what extern means. (Note that inline functions are a C99 or GNU extension; they weren't in original C.

For non-inline functions, extern is not needed as it is on by default.

Note that the rules for C++ are different. For example, extern "C" is needed on the C++ declaration of C functions that you are going to call from C++, and there are different rules about inline.


IOW, extern is redundant, and does nothing.

That is why, 10 years later:

  • A code transformation tool like Coccinelle will tend to flag extern in function declaration for removal;
  • a codebase like git/git follows that conclusion and removes extern from its code (for Git 2.22, Q2 2019).

See commit ad6dad0, commit b199d71, commit 5545442 (29 Apr 2019) by Denton Liu (Denton-L).
(Merged by Junio C Hamano -- gitster -- in commit 4aeeef3, 13 May 2019)

*.[ch]: remove extern from function declarations using spatch

There has been a push to remove extern from function declarations.

Remove some instances of "extern" for function declarations which are caught by Coccinelle.
Note that Coccinelle has some difficulty with processing functions with __attribute__ or varargs so some extern declarations are left behind to be dealt with in a future patch.

This was the Coccinelle patch used:

  @@
    type T;
    identifier f;
    @@
    - extern
    T f(...);

and it was run with:

  $ git ls-files \*.{c,h} |
    grep -v ^compat/ |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place

This is not always straightforward though:

See commit 7027f50 (04 Sep 2019) by Denton Liu (Denton-L).
(Merged by Denton Liu -- Denton-L -- in commit 7027f50, 05 Sep 2019)

compat/*.[ch]: remove extern from function declarations using spatch

In 5545442 (*.[ch]: remove extern from function declarations using spatch, 2019-04-29, Git v2.22.0-rc0), we removed externs from function declarations using spatch but we intentionally excluded files under compat/ since some are directly copied from an upstream and we should avoid churning them so that manually merging future updates will be simpler.

In the last commit, we determined the files which taken from an upstream so we can exclude them and run spatch on the remainder.

This was the Coccinelle patch used:

@@
type T;
identifier f;
@@
- extern
  T f(...);

and it was run with:

$ git ls-files compat/\*\*.{c,h} |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

Coccinelle has some trouble dealing with __attribute__ and varargs so we ran the following to ensure that no remaining changes were left behind:

$ git ls-files compat/\*\*.{c,h} |
    xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/'
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

Note that with Git 2.24 (Q4 2019), any spurious extern is dropped.

See commit 65904b8 (30 Sep 2019) by Emily Shaffer (nasamuffin).
Helped-by: Jeff King (peff).
See commit 8464f94 (21 Sep 2019) by Denton Liu (Denton-L).
Helped-by: Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 59b19bc, 07 Oct 2019)

promisor-remote.h: drop extern from function declaration

During the creation of this file, each time a new function declaration was introduced, it included an extern.
However, starting from 5545442 (*.[ch]: remove extern from function declarations using spatch, 2019-04-29, Git v2.22.0-rc0), we've been actively trying to prevent externs from being used in function declarations because they're unnecessary.

Remove these spurious externs.


The extern keyword informs the compiler that the function or variable has external linkage - in other words, that it is visible from files other than the one in which it is defined. In this sense it has the opposite meaning to the static keyword. It is a bit weird to put extern at the time of the definition, since no other files would have visibility of the definition (or it would result in multiple definitions). Normally you put extern in a declaration at some point with external visibility (such as a header file) and put the definition elsewhere.