代码之家  ›  专栏  ›  技术社区  ›  Jean-Francois T.

C(C99)中嵌套函数调用的限制

c99 c
  •  0
  • Jean-Francois T.  · 技术社区  · 6 年前

    根据C99,函数的嵌套调用是否有限制?

    例子:

    result = fn1( fn2( fn3( ... fnN(parN1, parN2) ... ), par2), par1);
    

    注意:此代码绝对不是一个好的实践,因为难以管理;但是,此代码是从模型自动生成的,因此可管理性问题不适用。

    4 回复  |  直到 6 年前
        1
  •  8
  •   Jonathan Leffler    6 年前

    没有直接的限制,但编译器只需要允许各种类别的一些最小限制:

    C11 standard :

    5.2.4.1平移限制 1实现应能够翻译和执行至少一个程序,该程序至少包含以下每个限制的一个实例: 18)

    ...

    • 63全表达式中带括号表达式的嵌套级别

    ...

    • 逻辑源行中有4095个字符

    18) 实现应尽可能避免强加固定的翻译限制

        2
  •  4
  •   haolee    6 年前

    不,没有限制。

    例如,这是一个C代码段:

    int func1(int a){return a;}
    int func2(int a){return a;}
    int func3(int a){return a;}
    
    void main()
    {
    func1(func2(func3(16)));
    }
    

    相应的装配代号为:

    0000000000000024 <main>:
      24:   55                      push   %rbp
      25:   48 89 e5                mov    %rsp,%rbp
      28:   bf 10 00 00 00          mov    $0x10,%edi
      2d:   e8 00 00 00 00          callq  32 <main+0xe>
      32:   89 c7                   mov    %eax,%edi
      34:   e8 00 00 00 00          callq  39 <main+0x15>
      39:   89 c7                   mov    %eax,%edi
      3b:   e8 00 00 00 00          callq  40 <main+0x1c>
      40:   90                      nop
      41:   5d                      pop    %rbp
      42:   c3                      retq   
    

    这个 %edi 寄存器存储每个函数的结果和 %eax 寄存器存储参数。如你所见,有三个 callq 与三个函数调用相对应的指令。换句话说,这些嵌套函数被逐一调用。无需担心堆栈。


    正如注释中提到的,当代码嵌套太深时,编译器可能会崩溃。 我编写了一个简单的Python脚本来测试这一点。

    nest = 64000
    
    funcs=""
    call=""
    
    for i in range(1, nest+1):
        funcs += "int func%d(int a){return a;}\n" %i
        call += "func%d(" %i
    
    call += str(1) # parameter
    call += ")" * nest + ";" # right parenthesis
    
    content = '''
    %s
    void main()
    {
    %s
    }
    ''' %(funcs, call)
    
    with open("test.c", "w") as fd:
        fd.write(content)
    

    nest = 64000 可以,但是 640000 将导致 gcc-5.real: internal compiler error: Segmentation fault (program cc1) .

        3
  •  1
  •   unalignedmemoryaccess    6 年前

    没有。因为这些函数是一个接一个执行的,所以没有问题。

    int res;
    res = fnN(parN1, parN2);
    ....
    res = fn2(res, par2);
    res = fn1(res, par1);
    

    执行是线性的,之前的结果将用于下一个函数调用。

    编辑:正如注释中所解释的,解析器和/或编译器在处理这种难看的代码时可能存在问题。

        4
  •  1
  •   Daniel H    6 年前

    如果这不是一个纯粹的理论问题,答案可能是“尝试重写代码,这样就不需要这样做,因为对于大多数正常的用例来说,限制已经足够了”。如果这纯粹是理论上的,或者你真的需要担心这个限制,不能只是重写,请继续阅读。


    C11标准第5.2.4节( latest draft, which is freely available and almost identical )指定实现的各种限制 必修的 支持。如果我读对了,你可以达到63级筑巢。

    然而,实现是 允许 支持更多,以及 在实践中 他们可能会这样做。我很难找到适合GCC的文档(我找到的最接近的文档是预处理器中的表达式),但我希望它没有硬性限制,除了编译时的系统资源。