216

In C, I did not notice any effect of the extern keyword used before function declaration. At first, I thought that when defining extern int f(); in a single file forces you to implement it outside of the file's scope. However I found out that both:

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

and

extern int f() {return 0;}

compile just fine, with no warnings from gcc. I used gcc -Wall -ansi; it wouldn't even accept // comments.

Are there any effects for using extern before function definitions? Or is it just an optional keyword with no side effects for functions.

In the latter case I don't understand why did the standard designers chose to litter the grammar with superfluous keywords.

EDIT: To clarify, I know there's usage for extern in variables, but I'm only asking about extern in functions.

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
Elazar Leibovich
  • 32,750
  • 33
  • 122
  • 169
  • According to some research I did when attempting to use this for some crazy templating purposes, extern is not supported in the form it's intended by most compilers, and so doesn't really do, well, anything. – Ed James May 13 '09 at 08:38
  • 7
    Its not always superfluous, see my answer. Any time you need to share something between modules that you do NOT want in a public header, it is very useful. However, 'externing' every single function in a public header (with modern compilers) has very little to no benefit, as they can figure it out on their own. – Tim Post May 13 '09 at 08:39
  • 1
    @Ed .. if volatile int foo is a global in foo.c, and bar.c needs it, bar.c must declare it as extern. It does have its advantages. Beyond that, you may need to share some function that you do NOT want exposed in a public header. – Tim Post May 13 '09 at 08:41
  • See also: http://stackoverflow.com/questions/496448/how-to-correctly-use-the-extern-keword-in-c/ – Steve Melnikoff May 14 '09 at 00:04
  • Duplicate of: http://stackoverflow.com/questions/10137037/extern-declaration-and-function-definition-both-in-the-same-file –  Jun 06 '13 at 07:54
  • 2
    @Barry If at all, the other question is a duplicate of this one. 2009 vs 2012 – Elazar Leibovich Jun 06 '13 at 11:58
  • Sure. Age is irrelevant in this context, and an Appeal to Novelty fallacy besides, since it's a discussion of a post System III C feature. The other one has vastly TL;DR succinct answers. –  Jun 06 '13 at 12:34

10 Answers10

174

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.

starball
  • 20,030
  • 7
  • 43
  • 238
Tim Post
  • 33,371
  • 15
  • 110
  • 174
  • 76
    Your code will compile just fine without the extern before the bar_function. – Elazar Leibovich May 13 '09 at 09:28
  • 1
    @Elazar , look out for broken compilers. – Tim Post May 13 '09 at 09:43
  • 2
    @Elazar, I already noted, prototyping functions as extern is rather useless with modern compilers :) – Tim Post May 13 '09 at 09:45
  • But can you elaborate on the old compilers thing. When is extern needed in old compilers? Is it standard? Was it inserted to the standard only to be backwards compatible to K&R C? – Elazar Leibovich May 13 '09 at 18:16
  • If a header declares a function `static` (it can, but very seldom - as in, essentially never - should), then the function in the public header is not 'extern'. – Jonathan Leffler Jun 15 '10 at 01:33
  • So 'extern' for functions could be considered syntatic sugar, like the 'auto' keyword for local variables, or the BASIC LET statement? – Arthur Kalliokoski Jun 15 '10 at 01:40
  • @Jonathan Leffler - I can't think of a case where a static declaration /prototype would be useful in a header? But yes, that's obvious. – Tim Post Jun 15 '10 at 03:36
  • 3
    @Tim: Then you've not had the dubious privilege of working with the code I work with. It can happen. Sometimes the header also contains the static function definition. It is ugly, and unnecessary 99.99% of the time (I could be off by an order or two or magnitude, overstating how often it is necessary). It usually occurs when people misunderstand that a header is only needed when other source files will use the information; the header is (ab)used to store declaration information for one source file and no other file is expected to include it. Occasionally, it occurs for more contorted reasons. – Jonathan Leffler Jun 16 '10 at 01:10
  • 4
    @Jonathan Leffler - Dubious indeed! I've inherited some rather sketchy code before, but I can honestly say I've never seen someone put a static declaration in a header. It sounds like you have a rather fun and interesting job, though :) – Tim Post Jun 17 '10 at 07:24
  • 1
    The disadvantage of the 'function prototype not in header' is that you do not get the automatic independent checking of consistency between the function definition in `bar.c` and the declaration in `foo.c`. If the function is declared in `foo.h` _and_ both files include `foo.h`, then the header enforces consistency between the two source files. Without it, if the definition of `bar_function` in `bar.c` changes but the declaration in `foo.c` is not changed, then things go wrong at run-time; the compiler cannot spot the problem. With a header properly used, the compiler spots the problem. – Jonathan Leffler Jul 29 '12 at 21:22
  • Side note: You don't need the `volatile` keyword in your example. The `sleep(30)` contains a [memory barrier](http://en.wikipedia.org/wiki/Memory_barrier). Also, a `static` function in header is actually useful. For example: `int f_complete(int x, int y); static inline int f_with_default(int x) { return f_complete(x, 0); }` In fact, most of the times `static inline` functions inside a header work like a macro. – Shahbaz Sep 22 '12 at 10:49
  • 1
    extern on functions declarations is superfluous like 'int' in 'unsigned int'. It's good practice to use 'extern' when the prototype is NOT a forward declaration... But it really should live in a header without 'extern' unless the task is an edge-case. http://stackoverflow.com/questions/10137037/extern-declaration-and-function-definition-both-in-the-same-file –  Jun 06 '13 at 07:51
  • @Tim "little point in specifying it with modern compilers"?Check out my Q : http://stackoverflow.com/questions/18171373/why-are-some-functions-declared-extern-and-header-file-not-included-in-source-in – rsjethani Aug 11 '13 at 14:01
  • If bar.c had both the function prototype and body, could foo.c still use it? – AlphaGoku Aug 30 '17 at 03:36
  • 1
    @TimPost Prior to C99, static functions in headers was the closest portable way to achieving the semantics of an inline function. Post-C99, many still use `static inline` as their default because of the fact that ISO C99 `inline` is incompatible with GNU C `inline`; only the combination `static inline` behaves the same in all flavours – M.M Oct 31 '17 at 03:32
  • 1
    The answer by @Rageous is actually sensible in the context of the question! – chao Feb 12 '18 at 17:37
  • 1
    @TimPost I must be missing something. First time main calls bar_function, the stop_now variable is zero. So the while loop in bar_function never terminates and bar_function never returns to main. Can someone please explain why I am mistaken? Thanks! – Razzle Dec 09 '20 at 13:07
  • 2
    @Razzle Wow, I can't believe you're the first person to catch that (me included!) That bug literally lurked in plain view for over 10 years. Gotta love it. Fixed :) – Tim Post Dec 09 '20 at 13:34
  • @Shahbaz Interesting example of static (helper) function in a header, thanks! But what does the 'static' achieve there? Normally I think of 'static' akin to 'private in module' or 'fixed reserved memory not on the stack' - but neither seems to apply in your helper function example. (On the 'volatile' issue, isn't it simpler to just say that volatile is superfluous bc it's a single threaded program? I opened the link but it's a lot to read..) – Razzle Dec 13 '20 at 10:52
  • 1
    @razzle, `static` makes sure that the copy of `f_with_default` in every file that the header is included in does not result in link error! Otherwise the linker thinks both files are defining a function with the same name and causes a link error. See also M.M's answer above on how C99 and GNU C `inline` differ. Presumably with C99 you don't need `static` (again presumably, because the symbol has weak linkage), but I'm not 100% sure right now. I've had trouble with weak symbols enough to rather avoid them. – Shahbaz Dec 14 '20 at 08:13
  • About `sleep` containing a memory barrier, I don't really remember what I had in mind at the time, but maybe I was thinking of something in the whole system call, scheduling etc aspect of sleep that incurs a memory barrier. Definitely worth being explicit about `volatile`. – Shahbaz Dec 14 '20 at 08:14
  • *every function in a library public header is 'extern', however labeling them as such has very little to no benefit, depending on the compiler* I don't see why it would depend on the compiler. Are there any circumstances where there'd be difference? I think other answers are right in saying the *extern* keyword has no effect when used with a function declaration. – Max Barraclough Mar 17 '21 at 17:26
  • @MaxBarraclough I've never experienced it first-hand, but both Borland and older versions of GCC _would_ reserve space on the first pass if not explicitly declared extern. As soon as I say nobody uses those anymore someone who has to work with them daily will chime in, but I think the case for it is exceedingly rare these days. – Tim Post Mar 17 '21 at 17:52
  • Adding extern before function declaration will make it more easy to find error like trying to use externally nested functions (you will have error directly pointing to nested function with extern prefix), also it will make more obvious that nested variant is not used (in case you tried to declare extern nested and already have one non-nested in other file) – Stanislav Orlov Mar 30 '22 at 07:37
98

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.

  • 27
    Then as the standard designer I'll *disallow* using extern with functions, as it just adds noise to the grammar. – Elazar Leibovich May 13 '09 at 09:22
  • 7
    Backwards compatibility can be a pain. – MathuSum Mut Jan 01 '17 at 14:53
  • 2
    @ElazarLeibovich Actually, in this particular case, _disallowing it_ is what would add noise to the grammar. – Lightness Races in Orbit Jul 06 '19 at 16:34
  • 2
    How limiting a keyword _adds_ noise is beyond me, but I guess it's a matter of taste. – Elazar Leibovich Jul 08 '19 at 17:42
  • 2
    It's useful to allow the use of "extern" for functions though, as it indicates to other programmers that the function is defined in another file, not in the current file and is also not declared in one of the included headers. – DimP Jun 25 '20 at 11:05
  • @DimP "Useful" is dubious. Best practice, as far as I understand it, is that you only put `extern` declarations (whether implicitly or explicitly marked as such) in header files. This is the classic advice of "program to an interface, not an implementation". They way to express "I require functions for doing foo-stuff" is to `#include "foo_stuff.h"`; you shouldn't instead write code that says "I require function `foo` to exist *somewhere*, with these specific args and return value". – josaphatv Aug 03 '20 at 18:01
  • _How limiting a keyword adds noise is beyond me_: I think that user Lightness Races in Orbit meant that this disallowing requires adding an extra logic/rules to the [C grammar](https://www.lysator.liu.se/c/ANSI-C-grammar-y.html), which prevent apply extern storage-class-specifier to functions. This extra logic/rules is considered to be a noise. – pmor Oct 05 '21 at 10:37
  • `extern` could be needed for headers with `inline` semantics. It would depend on the system supporting weak references or there might be multiple static declarations to satisfy the use of a function pointer. In the last case, `extern` is helpful. – artless noise Sep 15 '22 at 15:09
29

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.

YetAnotherUser
  • 527
  • 5
  • 12
  • Did you mean to remove the `int i = 2` line in -3rd paragraph? And is it correct to state, seeing `int i;`, the compiler will allocate memory for that variable, but seeing `extern int i;`, the compiler will NOT allocate memory but search for the variable elsewhere? – Frozen Flame Jul 21 '14 at 08:25
  • Actually if you omit the "extern" keyword the program will not compile because of redefinition of i in a.c and b.c (due to a.h). – Nixt Apr 28 '16 at 17:25
19

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.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • Is it the standard, or are you just telling me a typical compiler's behavior? In case of the standard, I'll be glad for a link to the standard. But thanks! – Elazar Leibovich May 13 '09 at 09:31
  • This is the standard behavior. C99 draft is available here: . The actual standard is not free though (the draft is good enough for most purposes). – dirkgently May 13 '09 at 09:36
  • 1
    I just tested it in gcc and it's both "extern int h();static int h() {return 0;}" and "int h();static int h() {return 0;}" are accepted with the same warning. Is it C99 only and not ANSI? Can you refer me to the exact section in the draft, since this doesn't seem to be true for gcc. – Elazar Leibovich May 13 '09 at 09:50
  • Check again. I tried the same with gcc 4.0.1 and I get an error just where it should be. Try comeau's online compiler or codepad.org as well if you don't have access to other compilers. Read the standard. – dirkgently May 13 '09 at 09:58
  • Checked at codepad.org see http://codepad.org/EZrxe4Ur There's an error when marked of course. However it also happens when NOT USING EXTERN. So extern have no effect in this case. – Elazar Leibovich May 13 '09 at 18:02
  • Not using extern means defaults to external linkage. – dirkgently May 13 '09 at 18:50
  • Thanks for quoting the standard, however if I understand it correctly, the standard says that not writing extern is just the same as writing extern. What you wrote is true, but is also relevant when just declaring a function. It is superfluous for functions then. – Elazar Leibovich May 14 '09 at 05:49
  • Yes, it is superfluous for function declarations. (I am not sure what your *real* question is. You seem to be flipping from one point to another. ) – dirkgently May 14 '09 at 06:25
  • 3
    @dirkgently, My real question is is there any effect for using exetrn with function declaration, and if there's none why is it possible to add extern to a function declaration. And the answer is no, there's no effect, and there was once an effect with not-so-standard compilers. – Elazar Leibovich Jun 15 '10 at 06:33
  • @ElazarLeibovich, perhaps the reason is for regularity. The fewer exceptions you have, the better life is. So the standard says "You can declare external symbols with `extern`". It doesn't bother to say "_except_ for functions". In the same way, I guess people like to say "I like to declare my external symbols with `extern`" and they don't bother to say "_except_ for functions". – Shahbaz Sep 22 '12 at 10:53
  • @ElazarLeibovich "is there any effect for using exetrn with function declaration": Well, don't forget what the std quoted above says. 'extern' can represent a static function if that function already has an existing declaration/definition in scope. I would assume that you would use nothing if you specifically want external linkage but use 'extern' if you aren't sure or prefer to control that knob elsewhere in the program. Thus, for functions, it seems 'extern' is always safe to use in declarations (C99) but may actually refer to a static (file scope) function, depending on context. – Jose_X Jan 08 '15 at 19:01
  • @ElazarLeibovich "is there any effect for using exetrn with function declaration"? [I am updating the earlier comment since I had made a mistake. The correct version:] The answer appears to be that, in C99 and for a function, it is irrelevant to use extern or not. In either case, the function may or may not be limited to file scope, ie, may or may not have external linkage (although usually it will have external linkage). In gcc, make sure to use -std=C99, although this particular feature I think was incorrect at least some years back. ..and C89 is different I think. – Jose_X Jan 08 '15 at 19:19
16

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.

user9876
  • 10,954
  • 6
  • 44
  • 66
  • 2
    This is the only answer here that both is correct and actually answers the question. –  Sep 06 '17 at 16:34
10

IOW, extern is redundant, and does nothing.

That is why, 10 years later:

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.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
3

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.

1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
2

In C, functions are implicitly defined as extern, regardless of whether or not the keyword is actually stated.

So, the code:

    int f() {return 0;}

The compiler will treat as

    extern int f() {return 0;}

Essentially, there is no semantic difference between a typical function definition and one preceded by the extern keyword, as in this example. You can read a more in depth explanation of this at https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

2

declaring a function extern means that its definition will be resolved at the time of linking, not during compilation.

Unlike regular functions, which are not declared extern, it can be defined in any of the source files(but not in multiple source files otherwise you'll get linker error saying that you've given multiple definitions of the function) including the one in which it is declared extern.So, in ur case the linker resolves the function definition in the same file.

I don't think doing this would be much useful however doing such kind of experiments gives better insight about how the language's compiler and linker works.

Rampal Chaudhary
  • 707
  • 1
  • 8
  • 18
  • 3
    IOW, extern is redundant, and does nothing. It would be much clearer if you put it that way. – Elazar Leibovich Jul 15 '11 at 06:46
  • @ElazarLeibovich I just encountered a similar case in our codebase and had the same conclusion. All those through answers here can be summed up in your one liner. It has no practical effect but might be nice for readability. Nice to see you online and not just in meetups :) – Aviv Apr 26 '18 at 07:34
1

The reason it has no effect is because at the link-time the linker tries to resolve the extern definition (in your case extern int f()). It doesn't matter if it finds it in the same file or a different file, as long as it is found.

Hope this answers your question.

Mac
  • 14,615
  • 9
  • 62
  • 80
Umer
  • 11
  • 1