代码:
typedef int a; // #1
extern int a; // #2, error
gcc会生成错误“重新声明为另一种符号'a'”,但是当我们将extern
声明移至块作用域时,将不会发出警告,为什么?
typedef int a; // #3
void foo() {
extern int a; // #4, ok
}
并且:
char a; // #5
void foo() {
extern int a; // #6, error
}
更新:
感谢@Yunnosch的回复,但仍然无法回答我的问题。让我们看一下#6,#5,当编译器看到#6时,它将尝试在文件作用域标识符中查找以查找是否存在相同的“ a”,尽管它们在不同的作用域中,但编译器会生成错误。 / p>
然后看#4,#3,当编译器看到#4时,它将以相同的方式发现相同的'a'存在,为什么它不生成错误?
@Yunnosch和@Stargateur都针对不同的范围进行了解释,这显然是不正确的。我的观点是关于链接的事情,但是#2无法隐藏#1告诉我这也不是事实。
更新2:
感谢@AnT,他给出了非常详细的解释。
答案 0 :(得分:1)
typedef名称和变量名称都是C中的普通标识符。它们共享相同的名称空间。您不能有两个名称相同的声明,它们在相同的作用域和相同的名称空间中声明不同的实体
6.2.1标识符范围
2 [...]指定了不同的实体 具有相同标识符的标识符要么具有不同的作用域,要么处于不同的名称空间。[...]
另外,如Stefan Ram在comp.lang.c 6.7 / 3中所建议的那样,在这里甚至更重要
6.7声明
3 如果标识符没有链接,则在相同的作用域和相同的名称空间中,标识符的声明(在声明符或类型说明符中)不得超过一个,但
/> —可以将typedef名称重新定义为表示与当前相同的类型, 前提是该类型不是可变修改的类型;
—可以按照6.7.2.3的规定重新声明标签。
无论哪种情况,关键是两个声明都在相同的作用域和相同的名称空间中进行。这是您的第一个代码示例违反的要求。这就是编译器所抱怨的。
您的第二个和第三个代码样本在不同的作用域中声明了两个标识符a
。那里没有违反6.2.1 / 2。这些示例可能还会遇到其他问题,但这是一个完全不同的故事。
如果您在不同的转换单元(在不同的文件范围内)定义全局a
,则其第二个示例可能是完全正确的,在该转换单元中其定义不会与typedef声明冲突。
您的第三个示例导致未定义的行为,因为a
的外部定义指定的类型与本地extern
声明不兼容。
6.2.7兼容类型和复合类型
2 所有引用相同对象或函数的声明均应具有兼容的类型;否则,行为是不确定的。
答案 1 :(得分:0)
如果文件范围对象或函数不需要在文件外部可见,则应通过声明为静态对象将其隐藏。这种做法创建了更多的模块化代码,并限制了全局名称空间的污染。
答案 2 :(得分:0)
C编译器不喜欢在范围内看到冲突的符号定义,
但不要介意冲突是否仅在不同范围内发生。
例如,甚至使用相同种类的符号来夸大其词:
示例1:基本,无警告,无错误
#include <stdio.h>
int a=0;
int main()
{
printf("%d\n", a);
return 0;
}
示例2:冲突范围相同
#include <stdio.h>
int a=0;
int a=5; // main.c 3 error: redefinition of 'a'
int main()
{
printf("%d\n", a);
return 0;
}
示例3:不同范围没有冲突
#include <stdio.h>
int a=0;
int main()
{
{
int a=5;
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
在示例3中,值为5的“内部” a
隐藏了外部a
。
值5从内部作用域中愉快地打印出来,隐藏了外部a
。
然后,当保留内部作用域时,将愉快地打印外部值0。它不再被内部作用域隐藏。
在您的第一种情况下,当两个a
在相同范围内时(如我的示例2),就会发生冲突(在您的类型不同的情况下)。
在您的第二种情况下,两个a
位于两个不同的范围内,并且没有冲突。
我的示例3显示,在那种情况下,即使它们具有相同的类型和相同的标识符,它们甚至不会冲突。
示例4:全局和外部局部之间的类型冲突 (交换类型,以继续示例序列)
#include <stdio.h>
int a=0;
int b=1;
static int c=10;
void foo() {
extern int b;
extern char a; // main.c 8 error: conflicting types for 'a'
extern char c; // main.c 9 error: conflicting types for 'c'
}
int main()
{
{
int a=5;
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
在示例4中,根据您的问题编辑,extern
告诉编译器使用在其他地方定义的a
,并明确告诉它也要在此函数范围内使用它。
但是,编译器已经看到a
的定义,并且(假设它是唯一的,因为否则将是重新定义的),它会抱怨不同的类型。请注意不同的错误,“冲突类型”而不是“重新定义”。
与此相反,请注意,上面类似的行带有b
,不会不发生冲突。告诉编译器知道一个b
,该类型在其他地方可以找到,并且类型相同。在外部全局范围中可以找到。
使用static
来使符号文件的作用域而不是全局作用域不会更改此设置,本示例使用c
进行演示。 external c
仍被识别为在此文件作用域中定义的具有不同类型的冲突c
。