c - C 调用具有空声明 arglist 的函数,使用 args 定义
问题描述
我刚刚解决了一个绝对令人头疼的问题,这个问题是如此简单,但却如此难以捉摸。如此令人沮丧地隐藏在缺乏编译器反馈和过度的编译器自满(这是罕见的!)背后。在写这篇文章的过程中,我发现了一些类似的问题,但没有一个完全符合我的情况。
- 当定义包含强类型参数时,调用不带无类型参数的方法会产生编译器错误。
- 为什么 gcc 允许将参数传递给定义为没有参数的函数?和具有不完整声明的 C 函数都将多余的参数传递给无参数函数。
- 为什么空声明适用于具有 int 参数的定义,但不适用于 float 参数?确实包含成功构建的声明/定义不匹配,但没有调用,我希望在其中看到一条
too few arguments
消息。
我有一个没有 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!
};
解决方案
函数从哪里得到缺少的参数?
通常,被调用的函数被编译为从参数将根据所使用的 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 没有在函数声明中提供参数信息,只是在定义中提供。仍然允许无原型声明,以便旧软件继续工作。
推荐阅读
- javascript - 选择特定值时将下拉值重置为默认值(选择)
- php - 无法将带有“.ics”的日历事件添加到 Outlook 365
- apache-kafka - 当消费者偏移量在段文件中时,Kafka 如何处理保留期到期的情况?
- firebase - 屏幕之间出现空错误时调用了 getter 'uid'
- mysql - 将 DATE() 与 SELECT 一起使用
- javascript - 我如何重构我的 JS 以使用更少的 if else 语句并减少重复
- vue.js - 使用 Vue CLI 3 启用 arcgis API JS 弹出窗口
- ios - iOS 上的 CloudKit + CoreData 和 watchOS 同步不起作用
- javascript - 如何使用 webpack 加载 CSS 文件
- mysql - 使用格式错误的 STR_TO_DATE 时插入警告