0

I was confused whether to use extern for forward declarations of functions in C. The scenario is each function is in separate .c/.cpp file. I understood from going through this question - External linkage of const in C , that if all are .c files I should not use extern for forward declaration irrespective of whether the function is defined in same file or not.

But I would like to know more about this when to explicitly use extern for forward declarations (I think when the function being forward declared is defined in a different language than calling one, extern would be required), and any caveats to be aware of.

Community
  • 1
  • 1
DevBee
  • 69
  • 2
  • 11
  • Functions are `extern` by default, you can use or omit this keyword from a forward declaration as you wish, the meaning doesn't change. – n. m. could be an AI Feb 11 '17 at 02:31
  • @n.m. using extern, the compiler ignores signature, I mean type checking is not done. So, it should not be used when not required. – DevBee Feb 11 '17 at 02:35
  • 1
    "the compiler ignores signature" Wrong. Who told you so? – n. m. could be an AI Feb 11 '17 at 02:37
  • @n.m. I am sorry, I misunderstood... – DevBee Feb 11 '17 at 03:02
  • "the compiler ignores signature" (perceived) problem happens when you make a forward declaration using extern inside a c file. The compiler is actually doing exactly what you ask, but your extern declaration may not match the actual function declaration in another file. The compiler doesn't check (and the linker doesn't either). This is why we place forward declarations in header files, rather than sprinkling them randomly across c files. That said: does `extern` in a c file modify the declation, or is it the default?, and why do people keep trying to do this! – Jamie Pate Aug 04 '17 at 00:00

3 Answers3

5

I think when the function being forward declared is defined in a different language than calling one, extern would be required.

I should warn you that your question is poorly phrased. I'm pretty sure you're interested in when to use

extern "C" int foo(int);

in C++, but I'm not sure, and I have to hope I'm not wasting my time.

Let's distinguish between compiler and linker. I'm leaving out some details, but nothing that affects the answer to your question.

A forward declaration is used by the compiler. In declaring the function, you give the compiler information about how it is used. You declare function F, and when the compiler runs across F being used, it knows what to do. (In the K&R days, in the absence of a declaration the compiler used defaults, sometimes leading to hilarious results. So now they're mandatory in both C and C++.)

Usually a forward declaration of a function is a functional prototype: it provides the parameter types. In C, that's not strictly necessary. You may write

int foo();

which tells the compiler foo is a function returning int, but not what parameters it takes. It then becomes your responsibility to get those right, because the compiler cannot check.

An extern declaration -- whether of a function or a variable -- notifies the compiler of a symbol and its type, and says the definition will be provided by another module. The compiler leaves a placeholder for the linker to fill later.

If you look at the getopt(3) man page, for example, you'll see optarg is declared extern; it's defined in the C runtime library, but you can use it because it's been declared.

Now we come to the linker, and the extern "C" of C++.

To the linker, a module exposes symbols. Global variables and functions not declared static have external linkage, meaning they can be used by other modules.

In C, there's a 1:1 correspondence between function names and external symbols. The name is the symbol. For example, getopt(3) has a symbol of the same name in the C standard library, libc:

$ nm -g $(find /usr/lib/ -name libc.a) 2>/dev/null | grep 'T getopt' 
0000000000001620 T getopt
0000000000000000 T getopt_long
0000000000000040 T getopt_long_only

In C++, the name is not the symbol. A C++ function can be overloaded; the same name can represent different functions taking different parameters. The compiler constructs a symbol that encodes the parameter types. Compare:

$ echo 'int foo(int foo) { return foo * foo; }' > a.c && cc  -c a.c && nm a.o
0000000000000000 T foo
$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T _Z3fooi

The encoding of the symbol name is often called name mangling. nm(1) has a demangling feature:

$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm -C a.o
0000000000000000 T foo(int)

Note that the parameter type appears next to the name.

In C++, extern "C" declares or defines the function as a C function. Here's a definition:

$ echo 'extern "C" int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T foo
  • In a declaration, it tells the compiler that the function is defined elsewhere, and to use a C symbol.

  • In a definition, it tells the compiler to emit a C symbol, i.e. not mangle the name, so that the function can be used by other C modules.

I hope that answers your question.

James K. Lowden
  • 7,574
  • 1
  • 16
  • 31
  • Thank you for writing such a thorough answer James. I'm writing some embedded code and having a c++ function called from assembly and this helped! – Turambar Dec 10 '20 at 06:04
4

In C language there's such thing as extern inline which has a special meaning and in which an explicit extern makes a difference.

Other than that, there's never any point in specifying an explicit extern in functon declarations, since functions in C and C++ always have external linkage by defualt.

The matter of "different language" is probably about such things as extern "C", but that's a completely different contruct from plain extern.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Thank you, that was what confused me. I mixed extern function declaration with extern "C" usage, and wanted to know when extern matters wrt function declarations. – DevBee Feb 11 '17 at 03:14
  • Does a function declaration in block scope make a difference: i.e `void f() { extern void g(); }`? – David G Feb 11 '17 at 06:16
2

Functions have external linkage by default, which means that

extern int foo(void);

and

int foo(void);

have exactly the same meaning. This is true for all function declarations, except those involving static in some way.

In particular, the presence or absence of extern has no effect on type checking, and whether or not the function is defined in a different language is irrelevant.

zwol
  • 135,547
  • 38
  • 252
  • 361