Why do the function prototypes use extern

Submitted by Eus
on May 18, 2008 - 8:14am

It is a universal truth that with or without the extern keyword a function prototype will just do the same. So, why does almost all function prototypes in Linux kernel source code prefixed with the extern keyword?

One possible reason is that because they were generated by an automated program that for the sake of clarity automatically prefixed each function prototype with the keyword. An example of this program will be cproto.

Otherwise, the use of the extern keyword is just a matter of coding style. Curiously enough, Linus does not spell this out in Documentation/CodingStyle

Archive: Linux Kernel Coding Style

> It is a universal truth

on
May 18, 2008 - 10:09am

> It is a universal truth that with or without the extern keyword a function prototype will just do the same.

"extern" is part of the linker stuff, not compiler. Don't know where you got that "u. truth", but IMHO for different objects (compilation units) extern is the only way to have function implementation available.

Prototype just tells to compiler, how to deal with parameters for function call.

Prefixing a function with "extern" or without means the same

on
May 22, 2008 - 6:31am

> "extern" is part of the linker stuff, not compiler.

IMHO, it is also a compiler's stuff because the compiler needs to process it and put the corresponding object code that the linker can process later.

> Don't know where you got that "u. truth"

I concluded that myself because, although there is a difference between declaring a global variable with or without extern storage class specifier, my textbook said that there is no difference between declaring a function prototype with or without one. To quote a paragraph on page 164 of Deitel & Deitel's C How to Program, 3rd edition:

There are two types of identifiers with static storage duration: external identifiers (such as global variables and function names) and local variables declared with the storage class specifier static. Global variables and function names are of storage class extern by default.

Moreover, I also want to demonstrate that there is no difference between applying or not applying storage class specifier extern to a function prototype. Below I have two C source code files: one has a function prototype prefixed with extern and the other one does not have one. As it can be seen, there is no difference at all in the generated assembly code files (cc -o assembly_code.S -S source_code.c). To conclude, there is no difference between applying or not applying extern to a function prototype.

extern int eus_var(int);

main()
{
        eus_var(0xBEEF);

        return 0;
}

        .file   "extern_var.c"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        subl    $12, %esp
        pushl   $48879
        call    eus_var
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.3.6"
int eus_var(int);

main()
{
        eus_var(0xBEEF);

        return 0;
}

        .file   "non_extern_var.c"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        subl    $12, %esp
        pushl   $48879
        call    eus_var
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.3.6"

> for different objects (compilation units) extern is the only way to have function implementation available.

I would be happy if you can give a concrete example of such a case.

prototypes

on
May 22, 2008 - 7:02am

1) prototypes are needed *only* in interfaces -- C header files (fix your coding style otherwise)
2) thus, extern must be used there -- in public API.

> > for different objects (compilation units) extern is the
> > only way to have function implementation available.

> I would be happy if you can give a concrete example of such a case.

You can link two objects without proper function prototypes from one to another, but see, what compiler will tell before that.

extern (i.e. linker) + hearers && cpp (API and software configuration) are incomplete (i'd say ah-hoc) parts of whole chain (big picture) of writing C code, configuring project, compiling, linking, using it.

All this is not "will just do the same", but "how to do" and "when to do": different configurations/API, static/dynamic linking, etc.

Still not explaining the point

on
May 26, 2008 - 4:56am

> 1) prototypes are needed *only* in interfaces -- C header files (fix your coding style otherwise)

Yes, I knew this already.

I put the function prototypes (i.e., extern int eus_var(int) and int eus_var(int)) in the .c files just for the sake of simplicity.

Even if I had put them in their corresponding header files (i.e., extern_var.h and non_extern_var.h) and included them in their corresponding .c files, it would not have made any difference at all because the C preprocessor will merge each of the header files with the corresponding .c file to make one translation unit before the compiler compiles them. Therefore, whether or not you put the function prototypes in the header files will not affect the result of the demonstration that I gave before. With other words, still, there is no difference between applying or not applying extern to a function prototype.

> 2) thus, extern must be used there -- in public API.

I think the use of word "must" is not apt. The apt word should be "may" because, as I have already pointed out before in the paragraph that I quoted from the Deitel & Deitel's book, "Global variables and function names are of storage class extern by default." So, because the default is extern, not writing the keyword extern will still cause the function declaration to have a storage class extern.

>> I would be happy if you can show a concrete example of such a case.

> You can link two objects without proper function prototypes from one to another, but see, what compiler will tell before that.

Yes, I knew this fact already. The compiler should say something like: "warning: implicit declaration of function `x'".

But, this is not a concrete example to show that applying or not the keyword extern to a function prototype will make any difference. I was expecting that you were going to say something like: "in machine A with compiler B, applying the extern keyword to a function prototype will cause something because it is a special extension to the ISO C Standard."

> extern (i.e., linker) + hearers && cpp (API and software configuration) are incomplete (i'd say ah-hoc) parts of whole chain (big picture) of writing C code, configuring project, compiling, linking, using it.
> All this is not "will just do the same", but "how to do" and "when to do": different configurations/API, static/dynamic linking, etc.

Still, this is not explaining the point that there is a difference between declaring a function prototype with or without the keyword extern.

I don't have the ISO C Standard at my disposal. But, the Deitel & Deitel's book should reflect what the ISO C Standard says. Therefore, if I should not have said "it is a universal truth". Instead, I should have said "according to the Standard". In this way, those who know that there are some implementations that extend the Standard will not have any objection.

I would be glad if you can help me with the following fact:
In Linux kernel 2.0.1 (it is almost invariant in Linux kernel 2.6.21.5), almost all function prototypes uses the keyword extern. But, looking at include/net/ip.h:

/*
 *      Functions provided by ip_fragment.o
 */

struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb
                                            , struct device *dev);
void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev
                                                            , int is_frag);

/*
 *      Functions provided by ip_forward.c
 */

extern int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag
                                                       , __u32 target_addr);

/*
 *      Functions provided by ip_options.c
 */

extern void ip_options_build(struct sk_buff *skb, struct options *opt
                              , __u32 daddr, __u32 saddr, int is_frag);
extern int ip_options_echo(struct options *dopt, struct options *sopt
                      , __u32 daddr, __u32 saddr, struct sk_buff *skb);
extern void ip_options_fragment(struct sk_buff *skb);
extern int ip_options_compile(struct options *opt, struct sk_buff *skb);

Yes, it is peculiar that the header file needs to make a special treatment of ip_fragment so that it says "Functions provided by ip_fragment.o" instead of "Functions provided by ip_fragment.c".

Now, if it is true that there is a difference between applying or not the keyword extern to a function prototype, this fact is worth explaining because Linux kernel has taken into account the portability to several architectures.

> I don't have the ISO C

on
May 26, 2008 - 1:40pm

> I don't have the ISO C Standard at my disposal.

Try to search this files (just for fun):

C_STANDARD-ISOIEC9899-1999.pdf
9899-1999_cor_2-2004.pdf
C99RationaleV5.10.pdf

I'd say kernel developement is pragmatic and not standard-based. Yet, kernel developers can't do much without compiler support, not only on asm instruction level, but also simple text processing.

> But, the Deitel & Deitel's book should reflect what the ISO C Standard says.

I have this reference, if something really messy stuff must be shuffled: http://www.joshuahawcroft.com/mirrors/c-standard/declare.html#Function%2...

Seems like 'extern' is used only by people, who care about image size/interfaces description or don't know about implicit linkage rules.

> I would be glad if you can help me with the following fact:
> In Linux kernel 2.0.1 (it is almost invariant in Linux kernel 2.6.21.5),
> almost all function prototypes uses the keyword extern.

It's not invariant in modern kernel. But explanation can be as simple as "it's a kernel internal header and allyesconfig/allmodconfig builds the kernel". Otherwise somebody will send a patch. Anyway it's better be asked in LKML.

As for static/extern games -- image size and clear interfaces described by more comprehensive means, than plain C doesn't bother many developers. Fuzzy logic and manual "make foo bar static" janitorial patches.

While i'm confused about standard meaning of 'extern' for function declarations, hope comments do some clearification.

Neat trick from Graphics Gems

SteveC (not verified)
on
July 18, 2008 - 11:57am

Speaking of extern, I saw a neat trick in the book "Graphics Gems" which lets you declare global variables and their extern declarations in the same header file, in the very same declaration.

You can have in a header:

#ifdef DECLARE_GLOBALS
#define GLOBAL
#define init(x) = x
#else
#define GLOBAL extern
#define init(x)
#endif

GLOBAL int some_var init(10);
GLOBAL int some_func();

#undef GLOBAL
#undef init

Then, in the implementation of the interface this header you include it by:

#define DECLARE_GLOBALS
#include "whatever.h"

But everywhere else (mere users of the interface, rather than the implemenation,) you include it by:

#include "whatever.h"

and leave out the define of DECLARE_GLOBALS.

That way, your implementation gets its globals allocated and initialized, and not declared "extern", and users of the header get only extern declarations without the initialization, but there is only _one_ declartion used by both -- so there are not two sets of declarations to keep in sync.

Umm... a neat trick?

on
July 23, 2008 - 8:06am

Hi Ho!

That way, your implementation gets its globals allocated and initialized, and not declared "extern", and users of the header get only extern declarations without the initialization,

If it is about variables, yes, you are right.
But, if it is about function prototypes, declaring only extern or not does not matter because in either case the function prototypes are treated as extern by default.

but there is only _one_ declartion used by both -- so there are not two sets of declarations to keep in sync.

Yes, this is valid only if you are talking about variables.
But, this is invalid if you are talking about functions. You still have to define the functions besides having their declarations in the header files. So, in this case, you have to retype the functions prototypes when you are defining the functions (i.e., there are two sets of "declarations" to keep in sync.)

Deriving from the given example, the code for the definition of some_var and some_func will be like the following one:

#define DECLARE_GLOBALS
#include "whatever.h"

int some_func() // Now you have to keep this in sync with the one in "whatever.h"
{
  ...
}

The aformentioned trick should not be used because the effort is not worth the benefit. The argument that you do not have to keep two sets of declarations in sync is inadequate because, whether or not this trick is employed, when you change the names of the variables or the functions, you still have to fix them everywhere.

The trick also limits the flexibility of the definition of each of the global variables. Specifically, the variables can only be defined in one translation unit. This may cause low-cohesion because not all global variables should be defined in a single translation unit. For example, my_robot.h defines global variables such as steps_per_second and eye_blinks_per_second. Naturally, steps_per_second should be defined in legs_controller.c, while eye_blinks_per_second should be defined in eyes_controller.c. In contrast, using the suggested trick, those variables must be defined in, for example, brain.c. However, brain.c should not define them. Instead, it should only initialize them according to the default values that the programmers can specify in an external file, for example, robot.conf or in a flash ROM.

The trick also clutters a translation unit because of multiple usages of #define DECLARE_GLOBALS when the translation unit needs to define global variables from more than one header file. For example,

#include "a.h"
#include "b.h"
#define DECLARE_GLOBALS
#include "c.h"
#include "d.h"
#define DECLARE_GLOBALS
#include "e.h"
#define DECLARE_GLOBALS
#include "f.h"

This is very likely to hamper the speed of a new maintainer of the code in understanding the code because the maintainer will lose concentration once #define DECLARE_GLOBALS is spotted.

Finally, the trick requires double effort to check which global variables are declared in a certain translation unit because a lookup to the related header files are needed. Futhermore, the trick makes the types of the global variables opaque from the programmer. This significantly reduces the readability of the translation unit. It is far cleaner and more transparent if the trick is not employed like what the following code illustrates:

#include "a.h"
#include "b.h"
#include "c.h"
#include "d.h"
#include "e.h"
#include "f.h"

int c_global_var = 99;
double e_global_var = 7.77;
struct something **f_global_var = NULL;

In short, I wonder how you perceive this trick as a neat one that can benefit you in any way.

A Function Prototype with or without `extern' is Identical

on
May 1, 2009 - 5:24am

Hi Ho!

While tuning on GCC development mailing list, I came across a post that discusses about the difference between `int i;' and `extern int i;' (http://gcc.gnu.org/ml/gcc/2009-04/msg00783.html). That reminded me of this blog post that seemed to lack a final conclusion. So, I asked the question there and got the definite answer (http://gcc.gnu.org/ml/gcc/2009-04/msg00812.html).

To conclude, a function prototype with or without keyword `extern' is completely identical.

Best regards,
Eus (FSF member #4445)

In this digital era, where computing technology is pervasive,
your freedom depends on the software controlling those computing devices.

Join free software movement today!
It is free as in freedom, not as in free beer!

Join: http://www.fsf.org/jf?referrer=4445

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.