外部声明遵循相同范围内的非外部声明
Extern declaration follows non-extern declaration in same scope
#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;
出现在函数内部时,它声明了一个与之前的 extern int x;
无关的 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
有外部链接(因为外部是没有存储 class 说明符的对象的默认声明,如 static
外部函数)。
- 它定义了一个
int
。 (其实是暂定的定义,这里不做赘述。)
这两个声明都说 x
是 int
的标识符并且具有外部链接。它们之间的区别是第一个没有定义对象(它只是说 x
是在其他地方定义的对象的名称),第二个确实定义了一个 int
(所以它是其他地方) .所以这些声明不冲突,是允许的。
另一方面,请考虑在函数内使用这些相同的声明。然后它们在块范围内。
那么extern int x;
的含义同上:x
是对别处定义的对象进行外部链接的标识符。
但是int x;
有不同的含义。它不是说 x
有外部(或内部)链接,而是说 x
没有链接,因为没有链接是块范围内声明的默认值。这会产生冲突,因为 C 2018 6.7 3 表示没有链接的标识符不得在同一范围内多次声明(和名称 space,此处未解决),但 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
语句会启动一个新块。
#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;
出现在函数内部时,它声明了一个与之前的 extern int x;
无关的 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
有外部链接(因为外部是没有存储 class 说明符的对象的默认声明,如static
外部函数)。 - 它定义了一个
int
。 (其实是暂定的定义,这里不做赘述。)
这两个声明都说 x
是 int
的标识符并且具有外部链接。它们之间的区别是第一个没有定义对象(它只是说 x
是在其他地方定义的对象的名称),第二个确实定义了一个 int
(所以它是其他地方) .所以这些声明不冲突,是允许的。
另一方面,请考虑在函数内使用这些相同的声明。然后它们在块范围内。
那么extern int x;
的含义同上:x
是对别处定义的对象进行外部链接的标识符。
但是int x;
有不同的含义。它不是说 x
有外部(或内部)链接,而是说 x
没有链接,因为没有链接是块范围内声明的默认值。这会产生冲突,因为 C 2018 6.7 3 表示没有链接的标识符不得在同一范围内多次声明(和名称 space,此处未解决),但 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
语句会启动一个新块。