如何解决Extern 声明跟在同一范围内的非 extern 声明之后
#include <stdio.h>
extern int x;
int main()
{
int x;
x = 1;
printf("%d",x);
}
#include <stdio.h>
int main()
{
extern int x;
int x;
x = 1;
printf("%d",x);
}
我正在学习 extern
关键字,有人告诉我单词 extern
用于声明变量,编译器不会为此变量分配。
但是当我写2个代码时,第一个代码运行正常,第二个代码出错。
请帮我解释为什么它有这种差异。谢谢。
解决方法
相同范围内的冲突声明
当 extern int x;
出现在函数外部时,它在文件范围内声明 x
。然后,当 int x;
出现在函数内部时,它会声明一个新的 x
实例,该实例与之前的 extern int x;
无关。这是 C 语言允许的,所以编译器不会抱怨。
当 extern int x;
出现在函数内部时,它在块范围内声明 x
。然后,当 int x;
出现在它之后时,它会尝试在同一范围内声明不同的 x
。这是C标准不允许的,所以编译器报错。
extern
关键字在这里不是特别相关——错误是由同一标识符的两个冲突声明引起的。例如:
char c;
int main(void)
{
char d;
int c; // Allowed,new declaration in new scope.
int d; // Not allowed,conflicting declaration in same scope.
}
关于声明的规则
由于 C 的发展历史,关于 C 声明的规则有一些不规范。在文件范围内考虑这些声明:
extern int x;
int x;
第一个声明做了(或不做)几件事:
- 它说
x
是int
的标识符。 - 它说
x
具有外部链接,这意味着它可以(在对象模块的链接期间)引用在其他地方声明的名为x
的对象。 - 它没有定义
int
。
对于第二个声明:
- 它说
x
是int
的标识符。 - 它说
x
具有外部链接(因为 external 是没有存储类说明符的对象的默认声明,例如static
外部函数)。 - 它定义了一个
int
。 (它实际上是一个暂定定义,但我们不会在这里处理。)
这两个声明都说 x
是 int
的标识符并且具有外部链接。它们之间的区别是第一个没有定义对象(它只是说 x
是在其他地方定义的对象的名称),第二个确实定义了一个 int
(所以它是其他地方) .所以这些声明不冲突,是允许的。
另一方面,考虑在函数内的这些相同的声明。然后它们在块范围内。
那么 extern int x;
的含义与上述相同:x
是一个标识符,带有外部链接,用于在别处定义的对象。
但是 int x;
有不同的含义。不是说 x
有外部(或内部)链接,而是说 x
没有链接,因为没有链接是块作用域中声明的默认值。这会产生冲突,因为 C 2018 6.7 3 规定,除了具有某些条件的 typedef
名称和标签外,在同一范围(和名称空间,此处未提及)中不得多次声明没有链接的标识符。
范围
C 有四种作用域:
- 文件范围用于函数外部的声明,并持续到正在编译的源文件的末尾。
- 块作用域用于函数内部的声明,并持续到块的结尾(下面讨论)。
- 函数原型作用域用于函数原型参数中的声明。 (例如,在
void foo(int n,float a[n][n]);
中,n
和a
具有函数原型作用域。) - 函数作用域是用于在
goto
语句中使用的标签。
复合语句是 {
和 }
内的声明和语句的列表。每个复合语句都是一个块,它为声明创建一个新的作用域。函数的主体是一个复合语句,它是一个块,它内部可能有额外的复合语句,每个复合语句开始一个新的作用域。
块也由 switch
、if
、do
、while
和 for
语句创建,但它们对于前四个语句基本上不重要那些,因为只有 for
语句提供了进一步声明的机会。例如,在 for
语句中,您可以编写 for (int x = 3; x < 20; ++x)
,这将创建 x
的新实例,因为 for
语句启动一个新块。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。