C 为什么调用外部函数指针会使程序崩溃?

C 为什么调用外部函数指针会使程序崩溃?,c,visual-c++,C,Visual C++,有两个源文件,a.c和b.c空调: int main(无效) { foo(); 返回0; } b.c: #包括 静态空心条(空心) { 放(“你好!”); } 外部无效(*foo)(无效)=巴; 将它们一起编译(cla.cb.c),运行程序,程序将崩溃。为什么呢 但是,类似于extern void(*foo)(void)的声明将解决问题 我的环境是Windows MSVC: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.0

有两个源文件,
a.c
b.c
<代码>空调:

int main(无效)
{
foo();
返回0;
}
b.c

#包括
静态空心条(空心)
{
放(“你好!”);
}
外部无效(*foo)(无效)=巴;
将它们一起编译(
cla.cb.c
),运行程序,程序将崩溃。为什么呢

但是,类似于
extern void(*foo)(void)的声明将解决问题

我的环境是Windows MSVC:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

我想这是因为:

  • 由于未声明
    foo
    ,编译器猜测它是一个函数
  • 但是,
    foo
    不是函数,而是变量,因此调用失败

  • 由于未声明
    foo
    ,编译器猜测它是一个函数。但是,
    foo
    不是函数,而是变量,因此调用失败

    在C99(检查请求)之前,C不会强制用户在调用函数之前声明函数,因此编译器不会发出警告或错误;更重要的是,实际上定义了一个
    foo
    ,无论它是变量还是函数,因此链接器没有警告或错误。这些使得这个问题很难找到

    但是,如中所示,有时这样的隐式函数调用会成为未定义的行为(UB):

    J.2未定义的行为

    -用于调用作用域中没有函数原型的函数,其中 函数由函数原型定义,或者 prototype以省略号或后面的参数类型结束 升级与参数的类型不兼容 (6.5.2.2)


    很抱歉,我意识到被骗是错误的。“C不会强迫用户在调用函数之前声明它们”-实际上它完全是这样,除非你使用的是一个过时的C版本。问题是,在这方面,实现并不总是很好地遵循C标准。@KonradRudolph谢谢你指出这一点,你提醒了我。C99之后有一个限制,我使用的是MSVC1998(像c89一样)。@KonradRudolph C是实现的,而不是标准所说的。只要隐式函数声明是所有广泛使用的C编译器在默认编译模式下接受的语言的一部分,它们就是C的一个特性。告诉人们它们不仅会引起混淆。@zwol我对混淆表示同情,但我坚持“C”是标准。这些实现不是C,而是标准的实现。这很重要,因为标准是规范性的,实现不是。特别是,当出现分歧时,缺陷就出现在实现中,需要在那里修复。如果只有一个主要的实现,那么这将变得不那么重要(或者更重要?),但事实并非如此。@KonradRudolph在编译器上提交错误报告时,这是一个很好的策略,但我们现在不这么做。当向初学者教授C语言时,这就是我们在这里所做的——在这个问题上没有语言律师的标签——你必须采取更务实的观点。