【问题标题】:C language calling nested functionC语言调用嵌套函数
【发布时间】:2016-09-25 20:35:34
【问题描述】:

我知道 C 不支持嵌套函数,它只是一个 gcc 扩展。但即便如此,这种行为还是很奇怪。

嵌套函数似乎只能调用一次;第二次调用会导致 SIGSEV,有时会导致 SIGILL。我想要堆栈等结构的嵌套函数。在堆栈上,我将能够定义诸如 pop、push 等函数,我将使用嵌套函数分配这些函数,这些函数将通过引用调用普通函数,我将得到这些函数。此函数类似于构造函数或初始化程序。但是这段代码足以模拟我的问题。

当 generate 函数将 innerFunction 赋值给 struct 时,第二次调用会导致错误。如果任务是测试功能,第二次调用就可以了。

请问哪里出了问题?在 gcc 文档中,它说只要你有内部函数地址,你就可以访问这个函数,并且嵌套函数可以访问上面定义的所有变量。

typedef struct A A;

struct A {
    void (*foo)();
};

void test() {
  printf("test\n");
}

void generate(A* a) {

    void innerTest(){
       test();
    }

    a->foo = &innerTest;
}


int main() {

    A a;
    generate(&a);

    a.foo();
    a.foo();
};

【问题讨论】:

  • 你的问题是什么?我正确编译并执行了你的代码。
  • 我相信嵌套函数是不符合标准的。即使它可能有效
  • documentation 实际上是说“如果你试图在包含函数退出后通过它的地址调用嵌套函数,一切都会崩溃。
  • 这段代码导致段错误并且只打印一次“test”。使用相同 gcc 版本的 c99 和 c11 进行测试。但是输出应该包含两次“测试”
  • GCC 手册有说明输出应该包含什么吗?如果您所看到的不是手册所说的,那么您与 GCC 开发人员有问题。 c99 和 c11 说“不要那样做”——你远远超出了他们的承诺。

标签: c gcc


【解决方案1】:

不允许指向内部函数的指针转义创建它们的堆栈帧。这会产生未定义的行为。

生成的代码在堆栈上放置了一个蹦床。使用您创建的特定构造,蹦床在您第一次调用该函数时被覆盖,因此它在第二次调用该函数时失败。

我个人很生气,因为它在没有闭包的情况下调用该行为,但这就是它的作用。哦,等等,我明白了。您的示例有效,因为在将它配对到这么远时,您不小心将其简化为工作代码(请参阅 acorngal 的评论)。您的真实代码访问标记为 a 的结构的指针,因此强制关闭并因此强制执行蹦床。你不能在 C 中做这个特技;在进行 OO 编程时,您必须自己传递 this 指针的等价物。

【讨论】:

  • 蹦床被创建,当嵌套函数的地址被获取时。我还是不明白,为什么蹦床会被覆盖。你能解释一下没有这种行为的原因和解决方案吗?
  • 一旦你从函数中返回,蹦床就会被释放。第一次调用有效,因为释放的内存还没有被覆盖。您正在尝试在 C 中编写一个不需要垃圾收集的垃圾收集构造。解决方案是重新设计以不依赖垃圾收集器。