在C语言中声明函数内部的函数

4
我在一次在线考试中发现了这个问题。以下是代码:
#include <stdio.h>

int main(void) {
    int demo();
    demo();
    (*demo)();
    return 0;
}

int demo(){
    printf("Morning");
}

我在考试后看到了答案。这是答案:
MorningMorning

我看了解释,但不明白为什么这是答案。我的意思是,int demo();这一行难道不会引起"问题"吗?

希望能有解释。谢谢。


1
@Jean-FrançoisFabre 这不是那个问题的重复。在函数内部声明函数与在函数内部定义函数非常不同。 - Petr Skocik
1
好的,已重新开放。下面的答案没有回答关于(*demo)();的部分。 - Jean-François Fabre
@Jean-FrançoisFabre,您能否请解释一下那部分内容(您可以发布一个单独的答案)。 - Debanik Dawn
4个回答

6

这并不是问题,因为int demo();不是函数定义,而只是外部声明,说明(声明)了存在这样一个函数。

在C语言中,你不能定义嵌套函数:

int main(void) {
    int demo() {} //Error: nested function!!!
}

但是你可以很好地声明一个函数。实际上,这相当于:

#include <stdio.h>

int demo(); //function declaration, not definition

int main(void) {
    demo();
    (*demo)();
    return 0;
}

int demo(){
    printf("Morning");
}

除了在您的代码中,外部前向声明 demo() 只在 main 内部可见之外,其他都一样。

如果我不“声明”函数会发生什么?我的意思是,我删除了int demo();这行代码,但它仍然可以正常工作。 - Debanik Dawn
4
啊,这是因为在C语言中有一个臭名昭著的“自动函数声明”,如果你使用一个没有先前声明的函数,编译器会为你创建一个函数声明,带上你传递的任何参数并返回int。也许在20世纪70年代很方便,但非常危险。打开你的编译器警告,你就会看到这种危险(并保持它们开启!)。 - rodrigo
谢谢,这很有帮助。 :) - Debanik Dawn

4
您可以在另一个函数内声明一个函数。这是允许的。唯一的问题是它只在声明它的函数内部可见。

2
其他答案没有解释清楚。
(*demo)();

部分。以下是部分解释:

demo(不带括号)是函数指针。因此,您可以首先对其进行解引用以获取一个函数,然后使用括号调用它(将解引用的对象包装到括号中,否则它意味着您要解引用函数的返回值

因此,它严格等同于

demo();

(这种符号在函数指针存储在变量中时非常有用,但在这里它只是比必要的更加神秘。)

如果demo()(*demo)()相同,那么这是否意味着demo*demo是完全相同的东西?这怎么可能呢? - Debanik Dawn
我写了一个程序,在其中创建了一个名为hello()的函数,然后在主函数中打印了hello*hello&hello的值。它们完全相同,我不明白。 - Debanik Dawn
这只是符号表示的问题。hello 是例程的地址。它不像普通变量。获取地址的地址或取消引用它都没有影响。 - Jean-François Fabre
啊,好的。我现在明白了。谢谢。 - Debanik Dawn

0

int main(void) { int demo(); /*...*/ }; int demo(){ /*...*/ } 等价于 int main(void) { extern int demo(); /*...*/ }; int demo(){ /*...*/ } 它在main函数内部声明了具有外部链接的demo函数。 demo标识符将在作用域结束之前有效,但demo将是一个普通的外部函数。

不幸的是,对于static函数,您无法这样做,因为标准(http://port70.net/~nsz/c/c11/n1570.html#note30)使得像 int main(void) { static int demo(); } static int demo(){ /*...*/ } 非法。

至于为什么(*demo)();可以工作并且在这种情况下等效于demo();(&demo)();(*&demo)();

声明了一个带有 int demo(); 的方法后,demo 是一个函数指示器(它与函数指针不同,就像数组指示器与指向数组第一个元素的指针不同一样)。与数组类似,函数指示器几乎总是衰变为函数指针(参见 6.3.2.1p4),这种衰变类型也发生在函数调用中。实际上,demo();demo 转化为指针并且调用函数指针。对这样的指针应用 * 再次产生函数指示器,然后再次衰变为指向调用的指针。

不可否认,所有这些东西都非常奇怪和深奥,我甚至想不到有什么情况下区别是有用的。在我看来,最好坚持使用demo();(demo)()(如果您想抑制宏扩展),永远不要使用(*demo)()(至少不要在函数上使用--在函数指针上使用还有一定的信息量),尤其不要使用(&demo)(),因为它除了让读者困惑之外没有任何作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接