12
#include <stdio.h>

static i = 5;

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

Can someone give any use-case for declaring a static variable as extern inside a function block?

NEW: Why is this not allowed?

int main()
{
static i = 5;
extern int i;
printf("%d\n",i);
return 0;
}
Bruce
  • 33,927
  • 76
  • 174
  • 262
  • 3
    Because `i` is either extern or local - it can not be both. – Till Mar 13 '12 at 14:44
  • not allowed? Your second code snippet runs perfectly fine on my gcc(mingw) – Pavan Manjunath Mar 13 '12 at 14:44
  • I am using gcc 4.1.2 20080704 (Red Hat 4.1.2-51) and I get the following error:file1.c: In function âmainâ: file1.c:6: error: extern declaration of âiâ follows declaration with no linkage file1.c:5: error: previous definition of âiâ was here – Bruce Mar 13 '12 at 14:46

2 Answers2

8

this is useful when you need to access a variable that resides within another translation unit, without exposing the external variable globally (for a few reasons, like name collision, or that the the variable shouldn't be directly accessed, so static was used to limit its scope, but that TU's header still needs access).

As an example, lets say we have a translation unit foo.c, it contains:

//foo.c
static int i = 0;

i shouldn't be changed or directly accessed outside foo.c, however, foo.h comes along requiring access to i for an inline function, but i shouldn't be exposed to any translation unit using foo.h, so we can use extern at functional level, to expose it only during the scope of IncI, the inline function requiring the use of i:

//foo.h
inline void IncI(int val)
{
    extern int i;
    i += val;
}

Your second example is 'disallowed' because the compiler thinks you are trying to bind two different variables to the same symbol name, ie: it creates the static i at local scope, but searches for the extern int i at global scope, but doesn't find it, because static i as at the function scope. a more clever compiler would just fix the linkage to the static i, whether or not this follows standards I wouldn't know.


Now that I have a C standards document to work from (shame on me I know...), we can see what the official stance is (in C99):

6.2.2 Linkages of identifiers

Section 3:

If the declaration of a file scope identifier for an object or a function contains the storageclass specifier static, the identifier has internal linkage.

Section 4:

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

thus, because static will cause internal linkage, the extern will bring that linkage into the current scope. there is also a footnote stating that this may cause hiding of variables:

23) As specified in 6.2.1, the later declaration might hide the prior declaration.

Necrolis
  • 25,836
  • 3
  • 63
  • 101
  • @pezcode: `static` can be used to restrict the visibility of an object to a translation unit, but a header with an `inline` function might need access to the static variable, thus the extern at function level comes into play, as the function can access the variable without 'globally' changing the visibility of the static variable – Necrolis Mar 13 '12 at 14:50
  • @Necrolis: What does foo.h contain finally? – Bruce Mar 13 '12 at 15:03
  • 1
    @Necrolis: You give an interesting example, but I wonder if it could really work that way, since what happens if some other translation unit `bar.c` also contains a `static int i = 0;`? How would the `extern int i;` inside `IncI()` know which `i` to refer to? I don't believe it can. – j_random_hacker Mar 13 '12 at 15:04
  • @j_random_hacker: I'm not sure, I've only ever used this method once, which I subsequently deleted for a better approach. You'll probably see the compiler try bind based on name and type, if it fails to select one then it'll throw a redefinition error (barring the use of things like `__declspec(selectany)`) – Necrolis Mar 13 '12 at 15:07
  • @Bruce: `foo.h` will finally contain the definition of `IncI`, with (optional) include guards. – Necrolis Mar 13 '12 at 15:09
  • @Necrolis: So where is the static variable? – Bruce Mar 13 '12 at 15:12
  • @Necrolis: A TU's header doesn't "know" which TU it corresponds to, does it? (I'm not sure about C, but in C++ it definitely doesn't.) And I believe a file-level `static` modifier prevents the declared object from being visible to any other TU, so any other TU that includes the declaration of `IncI()` ought to be unable to find the symbol. My guess is that if this worked for you then it might be that the language spec leaves this as "undefined behaviour" (or your compiler/linker had a bug, or you were only using your `inline` function from the TU defining `i`). – j_random_hacker Mar 13 '12 at 15:15
  • For the time being I'm -1ing, as I believe that even if this works, it's very poor practice -- it will either fail compilation/linking or give undefined behaviour if a second `static` definition of the same symbol name appears in an unrelated TU, when of course adding a `static` definition to a TU should never affect correctness of other TUs (for exactly the same reason that adding a local variable `j` in one function should not affect a local variable `j` in another function). – j_random_hacker Mar 13 '12 at 15:21
  • 1
    @j_random_hacker: I updated the question with the relevant sections of the C99 standards, which explain why it works and some pitfalls. I do agree this a bad practice, however, the question wasn't about practice, but about its use. – Necrolis Mar 13 '12 at 15:27
0

In your second case, you are telling the compiler you have a static (local) variable i and another (global) variable i that is defined somewhere else.

len
  • 335
  • 1
  • 5
  • because in the first case the `static i = 5` was outside of the code block, ie. external. – len Mar 13 '12 at 15:10
  • I think it is because when we write static int i = 5 inside a function it has no linkage. However, when written outside it has internal linkage. – Bruce Mar 13 '12 at 15:14
  • Variables that have file scope are essentially global and can be used in other files by use of extern. Variables that have block scope cannot be expected to be in scope (ie exist) when execution is in another code block in another file. So the compiler expects an `int` named `i` but can't find it because it is 'hidden' away in a code block – len Mar 13 '12 at 15:23