Why 'extern' storage class works different

2020-04-13 00:52发布

The following snippet works fine

extern int i;
int i;

int main(){
    return 0;
}

Here what I got is, 'i' is declared and then defined. Since there is only one definition so thats perfectly fine.

int main(){
    extern int i;
    int i;
    return 0;
}

Now, the above one gives the following error

new.cpp: In function ‘int main()’:
new.cpp:5:6: error: redeclaration of ‘int i’
  int i;
      ^
new.cpp:4:13: note: previous declaration ‘int i’
  extern int i;

Whats the problem here? Here also there is single definition of 'i'.

2条回答
Melony?
2楼-- · 2020-04-13 01:23

To understand the difference, you need to get familiar with a concept called tentative definition in C. To quote the C standard:

C11, draft, §6.9.2, External object definitions

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

What you have in the first snippet is only a tentative definition for i. You can have as many tentative definitions for an object as you want (but only one definition is allowed):

int i; // tentative definition
int i; // tentative definition
int i; // tentative definition

int main(void) {
   return 0;
}

is valid.

Here, i has external linkage and tentatively defined. If i is defined in somewhere in the same translation unit, then that'll be the actual definition of i. If there's no other definition of i is found in the translation unit, then this becomes the full definition as if it was defined like:

int i = 0;

int main(void) {
   return 0;
}

But the second snippet int i; is not a tentative definition. Only objects with external linkage can be defined tentatively. In second snippet, The declaration extern int i; says i is defined elsewhere with external linkage. But the next line int i; says i is defined with no linkage (local automatic variables do not have any linkage -- this is not a tentative definition). So there's a conflict of definition for i. Hence, the first one snippet is fine but second isn't.

查看更多
成全新的幸福
3楼-- · 2020-04-13 01:47

In the second case, there are two declarations of i in one scope. One says "there is a variable i defined outside this function"; the other says "there is a variable i defined inside this function". Without a new scope, that isn't allowed.

The rules are different inside and outside functions.

Note that you could use:

#include <stdio.h>

int i = 21;

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

This compiles OK (unless you include -Wshadow in your compilation options when using GCC or Clang), and produces 57 and 37 on the output (as pointed out by CoffeeAndCode in a comment).

See also How do I use extern to share variables between source files?

查看更多
登录 后发表回答