为什么这段代码会导致编译错误“重新定义”?

时间:2012-07-15 14:31:28

标签: c function-pointers

此代码导致编译错误“错误:使用不同类型重新定义'p'”:

void fun() {
    printf("fun");
}
void (*p)();
p = &fun;

但如果修改
void (*p)(); p = &fun;
void (*p)() = &fun,一切都很好。


有什么区别 void (*p)(); p = &fun;
void (*p)() = &fun

4 个答案:

答案 0 :(得分:3)

您无法在全局范围内执行任意分配;尝试:

void fun() {
    printf("fun");
}

void (*p)();

int main(void) {
 p = &fun;
 return 0;
}

void (*p)() = &fun;有效,因为您正在创建和初始化变量。全局范围内允许初始化。 void (*p)(); p = &fun;创建一个未初始化的变量,然后为其分配值。分配与初始化的处理方式不同,需要在某个函数内部执行。

答案 1 :(得分:2)

void (*p)();   // This is a declaration

void (*p)() = &fun;  // This is also a declaration

是声明。

p = &fun;  // This is a statement

是一份声明。声明不是声明(声明不是声明)。

您不能在文件范围内拥有语句。 C只允许在块范围内使用语句。

答案 2 :(得分:2)

只有声明和函数定义在全局范围内有效;作业不是。

不同之处在于int n = 1;是一个初始化声明,而n = 1;是一个赋值。前者在全球范围内有效,而后者则不然。函数指针也是如此。

作为一项规则,您应该始终更喜欢初始化到分配,这可以解决您的问题:

void fun() { /* ... */ }

void (*p)() = &fun;  // declaration, definition and initialization of "p"

答案 3 :(得分:1)

之前的三个答案没有回答问题,并且当他们表示“p =& fun;”是一项任务时不正确。

实际上,编译器试图将“p =& fun;”解释为声明,因此“p”是声明符,“& fun”是初始化器,“p =& fun”形成一个init-declarator(在C规范的正式语法中)。

将此解释为声明后,声明中应该出现的类型默认为int(由于遗留原因),因此编译器基本上将其视为“int p =& fun;”,这是p的定义。因为p以前被定义为函数的指针,所以编译器抱怨你用另一种类型重新定义了p。

对于语言语法学家:我无法弄清楚这可能是正式语法中的声明。翻译单元将扩展为外部声明,外部声明将扩展为声明,声明将扩展为“declaration-specifiers init-declarator-list [opt];”(C标准中的6.7) 。声明说明符似乎至少需要存储类说明符,类型说明符,类型限定符或函数说明符中涉及的一个关键字。源中没有出现这样的关键字,因此这不是声明。我怀疑编译器正在使用1999标准之前的一些语法进行解析,因此这是遗留行为。尽管如此,错误消息清楚地表明编译器已经看到了两个定义,而不是一个定义后跟一个赋值。