🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.c.moderated
Article #2164

"extern" and function declarations

#2164
From: Lawrence Kirby
Date: Mon, 04 Mar 1996 04:12
168 lines
6936 bytes
In article <4h75dc$4nv@solutions.solon.com>
           aril@cmt.lpr.mail.carel.fi "Ari Lukumies" writes:

 > The scope modifier 'extern' is used to indicate that a particular function does
 >  
 > not exist in the currently compiled source module, but in another one. By 
 > default, if you leave out 'extern' and 'static', the functions are compiled so,
 > that in the produced object code they are 'published',

This is an area in C that is very mixed up and is often misunderstood.
Unfortunately everything you say above is incorrect. (Sorry!)

Firstly extern does not affect scope in any way, in fact where functions
are concerned it makes no difference at all. Whether you use it or
not is entirely a stylistic issue. For example:

    extern int foo(void);

means *exactly* the same thing as:

    int foo(void);

And perhaps more surprisingly:

    extern int bar(void)
    {
        return 0;
    }

means exactly the same thing as:

    int bar(void)
    {
        return 0;
    }

So for function declarations/definitions extern is redundant. All four of
these declarations have what is termed external 'linkage'. What this means
is that two external declararations of the same identifier anywhere in
the program (possibly different files/translation units) refer to the
same thing (object or function). Scope simply defines where the identifier
in a particular declaration is visible (and is only applicable within a single
file/translation unit).

 > ie. that another object 
 > module may call them and the linker can find them and fix the calling address. 
 > If you declare a function 'static', many compilers do not include the 
 > publishing information for them, so the linker cannot find them.

Typically a C compiler will 'publish' an identifier to the linker if it has
external linkage and it is defined in that file (i.e. storage is allocated).
extern can affect this in object declarations (see below) but not function
declarations.

 > If your 
 > functions are called from within one module only, declaring them 'static' 
 > reduces the amount of object code produced.

It might reduce the size of an external symbol table but that's about it.
static avoid the possibility of name conflicts between different translation
units (what you call modules). You can be sure that a statically declared
function won't clash with anything declared in a different translation
unit (but there may still be clashes with identifiers in header files
included as part of the same translation unit).

 > Some compilers accept the case 
 > where you declare a function 'extern' everywhere, but that is not a good 
 > practice, if you consider someone who has to maintain your code afterwards.

I think you are saying that some linkers accept multiple definitions of
identifiers. That's fine but as far as C is concerned this results in
undefined behaviour so should be avoided whether it works with your
particular implementation or not.

I mentioned object declarations above. Consider the following (assume
everything from here on is declared outside of any function):

    int i = 1;

The simplest case - this is a declaration that reserves space and hence
is also a definition. It has file scope i.e. is visible up until the end
of the translation unit. It has external linkage i.e. an int i with external
linkage in a different translation unit will refer to the same object as
this one. The object is allocated at program startup and initialised with the
value 1.

    extern int i = 1;

This means exactly the same thing as int i = 1; The initialisation 'forces'
the definition (allocation of space).

    extern int i;

This is a declaration but not a definition. If the object that this
declaration refers to is actually used somewhere in the translation unit
then there must be a definition for that object somewhere else in the
program (which may be in the current or a different translation unit).
This makes it useful for header files since you can have:

    extern int i;

in a header file and:

    int i = 1;

in one of the source files to define it. If the source file includes the
header file both will exist in the same translation unit and the compiler
can validate directly whether they are compatible.

    int i;

This one is trickier than it first seems and you'll notice that I didn't
start with it. This is termed in the standard as a 'tentative definition'.
It can exist in a translation unit along with other declarations for
the identifier including a 'full' definition. So the following lines in a
single source file are legal:

    extern int i;
    extern int i;
    int i;
    int i;
    int i = 1;

A 'tentative definition' becomes a definition if you get to the end
of the translation unit without encountering a 'full' definition
(i.e. with an initialisation). Tentative definitions are those
that you recognise as being implicitly initialised to zero (but
as you can see from the example that doesn't always happen).
int i; in a translation unit ensures that i will be defined by
that translation unit so you can't have int i in two translation units
because there would then be two definitions for the same object in the
program.

It is also worth considering:

    static int i;

and:

    static int i = 1;

The first is a tentative definition (so it fits my implicit initialisation
observation above) however they have internal linkage so what happens in
other translation units is not relevant. This simplifies matters and
it is enough to observe that you can write:

    static int i;
    static int i;
    static int i = 1;

in a single translation unit, but you can't mix these with the declarations
above that have external linkage.

I'm curious - do people find this enlightening (you learnt something),
obvious (you knew it already, comp.std.c regulars need not apply),
unintelligible (I didn't explain it very clearly) or uninteresting (not
worth the bandwidth)?

I wonder how many C books or courses cover things like tentative
definitions (in whatever terminology). K&R2 discusses this in the language
reference in appendix A (A10.2) but not, as far as I can tell, in the
main tutorial.

-- 
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------
Technical Details
Message-ID: <4hdqjq$8pr@solutions.solon.com>
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
From: fred@genesis.demon.co.uk (Lawrence Kirby)
Newsgroups: comp.lang.c.moderated
Subject: "extern" and function declarations
Date: 3 Mar 1996 22:12:42 -0600
Organization: none
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4hdqjq$8pr@solutions.solon.com>
Reply-To: fred@genesis.demon.co.uk
NNTP-Posting-Host: solutions.solon.com
X-Mailer: Demon Internet Simple News v1.27