20

I have been wondering whether an extern can be declared locally and a register variable. If it can be what would be the restrictions imposed?

5 Answers 5

19

Local variables can be declared extern in some cases

Let's read the C99 N1256 standard draft.

The standard calls "local variables" as having "block scope".

6.7.1/5 "Storage-class specifiers" says:

The declaration of an identifier for a function that has block scope shall have no explicit storage-class specifier other than extern.

Then for what it means to add extern to a local variable, 6.2.2/4 "Linkages of identifiers" says:

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.

Lets break down those cases.

no prior declaration

void f() {
    extern int i;
}

is the same as:

extern int i;
void f() {}

except that the declaration is only visible inside f.

This is because i has no prior declaration visible. So i has external linkage (the same linkage as global variables).

prior declaration specifies no linkage

int i;
void f() {
    extern int i;
}

is the same as:

void f() {
    extern int i;
}

because the prior declaration int i specifies no linkage because paragraph 6 says:

The following identifiers have no linkage: an identifier declared to be anything other than an object or a function; an identifier declared to be a function parameter; a block scope identifier for an object declared without the storage-class specifier extern.

prior declaration specifies internal or external linkage

extern int i;
void f() {
    extern int i;
}

is the same as:

extern int i;
void f() {}

and:

static int i;
void f() {
    extern int i;
}

is the same as:

static int i;
void f() {}

because in both cases we have a previous visible external and internal (static) linkage declarations respectively.

Initialize local extern

Invalid C:

void f() {
    extern int i = 0;
}

because the block scope declaration has an initialization.

Valid C:

extern int i = 0;
void f() {}

but arguably bad style because equivalent to the shorter:

int i = 0;
void f() {}

because 6.7.8 Initialization says:

If the declaration of an identifier has block scope, and the identifier has external or internal linkage, the declaration shall have no initializer for the identifier.

Sign up to request clarification or add additional context in comments.

8 Comments

Necroposting, but - ISO/IEC 9899:1999 6.7.1/p5 isn't talking about block scope variables, it's talking about block scope functions. It's saying void f() { static void g() {} } is invalid, only extern can be specified for block scope function declarations.
@blelbach thanks for that. Is there an analogous quote for variables, or is the answer wrong? If there is an analogous quote for variables, can you edit the answer with it?
Under “prior declaration specifies no linkage,” this answer says int i; extern int i; is the same as extern int i;. However, it is not, because the behavior is undefined. C 2018 (and 2011 and earlier) 6.7 3 gives the constraint that, if an identifier has no linkage, there shall be no more than one declaration of it in the same scope and namespace, with certain exceptions for typedef names and tags. Since the i of int i; has no linkage, there shall be no other declaration of it in the same scope. When this constraint is violated, the behavior is undefined.
C 2018 clause 6.7 paragraph 3 says “If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except that: — a typedef name may be redefined to denote the same type as it currently does, provided that type is not a variably modified type; — tags may be redeclared as specified in 6.7.2.3.”
Yes, moving int i outside the function moves it to a new scope. (Each compound statement starts a new block [the block for the compound statement that defines a function actually starts at the start of the parameter declarations], as do selection and iteration statements and their substatements.) However, when int i; is outside any function, it does not have no linkage; it has external linkage. For an example with no linkage followed by external linkage that is not a constraint violation, you could have void f(void) { int i; { extern int i; … } }.
|
12
  1. Can local variables be declared extern?

No. But a global variable can be declared extern locally.

// file1.c
int Count;

// file2.c
void foo(void) {
  extern int Count;
  Count++;
}
  1. Can register variables be declared extern?

No. A variable may not be extern and register.

C11 dr 6.7.1 Storage-class specifiers
1 storage-class-specifier:
typedef
extern
static
_Thread_local
auto
register
Constraints
2 At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern)

Comments

3

6.9 External definitions of C99 states:

The storage-class specifiers auto and register shall not appear in the declaration specifiers in an external declaration.

1 Comment

The OP's question paragraph is not very clear, but the title captures the essence; I believe this doesn't answer what he/she asked.
1

You only are allowed to define a global variable as extern. Telling the compiler (and linker) that it is defined elsewhere.

A local variable only exist in the local scope, as it is created on the stack or in a register. When the execution is not in the scope (anymore) the stack is unrolled (so free space becomes available again) or the register is used for other things, and the variable does not exist (anymore).

So defining a local extern would be 'weird' and impossible (due to the stack usage).

5 Comments

Well technically you don't 'make it extern' but more tell the compiler that this variable isn't defined in this file?
Hm... you are right. You define a variable extern to tell the compiler that it is created somewhere else already and that the linker need to find it while linking, so it can be used (even though not explicitly defined in the source file that is using it). I updated my answer, to make it more clear!
Must add though that it is of course valid to access a variable like extern int a; from function scope as long as the variable is defined elsewhere in global scope. You don't need to put extern int a; in global scope in your file if you only need to access it from one function.
@Jite: (in answer to your first comment) technically I think the language in the standard is that the declaration results in a name with external linkage. That doesn't actually mean it's not defined in this file, you're allowed to follow up an extern declaration with a definition if you want to. And a good thing too, otherwise you couldn't include a library's "own" header file into the library source file prior to defining its globals :-) What external linkage does mean is that if two different TUs use identical names, both having external linkage, they refer to the same object/function.
@SteveJessop: Sure, thanks for clarifying. My first comment wasn't actually a statement but a question, so thanks for answering :)
1

The phrase register variable is not clearly to me, so I would take one bold guess on what OP is really curious about, and rephrase the original question as: Could local variables be declared with extern specifier?, illustrated by the following snippet:

int main() {
    extern int x; // Is this OK?
    return 0;
}

The answer is yes.

scope (visibility) and storage are two independent and connected concept. Here, x is one local variable (scope), and it's only visible within this block. extern dictates the storage, meaning this is merely one declaration, this variable is defined somewhere else. Would recommend the C standard for definite reference.

As for the omitted register part, I assume OP meant one variable with register storage-class-specifier, like register int x. Then, it's illegal to specify register and extern at the same time.

int main() {
    extern auto int x; // This is wrong.
    return 0;
}

At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.

The symmetric question would be: is it valid to specify auto or register with global or external variables, and this is exactly what Alexey Frunze's answer is about.

auto int x; // This is wrong.
int main() {
    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.