首页 > 解决方案 > C 调用具有空声明 arglist 的函数,使用 args 定义

问题描述

我刚刚解决了一个绝对令人头疼的问题,这个问题是如此简单,但却如此难以捉摸。如此令人沮丧地隐藏在缺乏编译器反馈和过度的编译器自满(这是罕见的!)背后。在写这篇文章的过程中,我发现了一些类似的问题,但没有一个完全符合我的情况。

我有一个没有 args 的函数声明,一个没有 args 的对该函数的调用,以及下面带有 args 的函数定义。不知何故,C 设法成功地调用了该函数,没有警告,没有错误,但是非常未定义的行为。函数从哪里得到缺少的参数?由于未定义 no-arg 函数,为什么我没有收到链接器错误?为什么我没有收到编译器错误,因为我正在重新定义具有不同签名的函数?为什么,为什么,这是允许的?

编译为 C++ 代码 ( gcc -x c++,在 Godbolt 上启用 Compile To Binary) 我得到一个链接器错误,因为 C++ 当然允许重载,并且没有定义无参数重载。通过检查 Godbolt,使用 Clang 和 MSVC 作为 C 代码编译也都成功构建,只有 MSVC 吐出一个小警告。

这是我对 Godbolt 的简化示例。

// Compile with GCC or Clang -x c -Wall -Wextra
// Compile with MSVC /Wall /W4 /Tc

#include <stdio.h>
#include <stdlib.h>

// This is just so Godbolt can do an MSVC build
#ifndef _MSC_VER
#  include <unistd.h>
#else
#  define read(file, output, count) (InputBuffer[count] = count, fd)
#endif

static char InputBuffer[16];

int ReadInput(); // <-- declared with no args

int main(void)
{
    int count;
    count = ReadInput(); // <-- called with no args

    printf("%c", InputBuffer[0]); // just so the results, and hence the entire function call,
    printf("%d", count);          // don't get optimised away by not being used (even though I'm 
    return 0;                     // not using any optimisation... just being cautious)
};

int ReadInput(int fd) // <-- defined with args!
{
    return read(fd, InputBuffer, 1); // arg is definitely used, it's not like it's optimised away!
};

标签: cfunctioncompilationdeclarationsignature

解决方案


函数从哪里得到缺少的参数?

通常,被调用的函数被编译为从参数将根据所使用的 ABI(应用程序二进制接口)传递的位置获取其参数。当被调用函数在单独的翻译单元中时(并且没有链接时优化),这必然是正确的,因此编译器无法根据调用代码对其进行调整。如果调用和被调用函数在同一个翻译单元中,编译器可以做其他事情。

例如,如果 ABI 说第一个int类参数是在处理器寄存器中传递的r4,那么被调用的函数将从寄存器中获取其参数r4。由于调用者没有在其中放置参数,因此被调用的函数会r4从之前的使用中获取任何值。

由于未定义 no-arg 函数,为什么我没有收到链接器错误?

C 实现通常仅通过名称解析标识符。类型信息不是名称的一部分或解析的一部分。声明为int ReadInput()的函数与声明为 的函数具有相同的名称int ReadInput(int fd),并且就链接器而言,其中一个的定义将满足对另一个的引用。

为什么我没有收到编译器错误,因为我正在重新定义具有不同签名的函数?

定义是兼容的。在 C 中,声明int ReadInput()并不意味着函数没有参数。它的意思是“有一个名为ReadInput返回的函数int,我不会告诉你它的参数是什么。

声明的int ReadInput(int fd)意思是“有一个名为ReadInput返回的函数int,它接受一个参数,一个int。这些声明是兼容的;两者都没有说任何与对方不一致的东西。

为什么,为什么,这是允许的?

历史。最初,C 没有在函数声明中提供参数信息,只是在定义中提供。仍然允许无原型声明,以便旧软件继续工作。


推荐阅读