提问人:ll cool 提问时间:11/4/2023 最后编辑:chqrliell cool 更新时间:11/4/2023 访问量:96
C 中的默认参数升级导致错误,但我不知道确切的原因
Default argument promotions in C cause error,but I don't know exactly why
问:
下面的代码示例摘自 K. N. King 的 C 编程。
一种现代的方法。它无法正常工作:输出是 而不是 .笔者说,这是默认参数提升导致的问题:1
9
在调用 square 时,编译器还没有看到原型,所以它不知道 square 需要 type 的参数。相反,编译器会在 上执行默认参数升级,但不起作用。由于它需要一个类型的参数,但已经给出了一个值,
int
x
int
double
但是他没有给出一个非常具体的解释,我想知道问题到底是怎么发生的?
#include <stdio.h>
int main(void)
{
double x = 3.0;
printf("Square: %d\n", square(x));
return 0;
}
int square(int n)
{
return n * n;
}
我运行了该程序,但它不起作用,但我想知道运行时中究竟是什么问题导致了它。
答:
首先,题中的代码不是有效的 C99,更不用说 C11、C18 或 C23 了。在 C99 及更高版本中,函数必须在使用之前声明(main() 除外)。只有 C90 允许在不声明的情况下使用函数 - 但这不是一个长期标准,目前已经过时了。您必须在调用之前声明该函数。
也就是说,给定代码段中的另一个问题正是报价中提到的,您在预期时发送了一个值。由于 和 不是兼容的类型,因此此程序调用未定义的行为。输出不能以任何方式证明其合理性。double
int
double
int
引用 C 标准,第 6.5.2.2/P6 章
...如果使用包含原型的类型定义函数,并且原型以省略号 (, ...) 结尾,或者升级后的参数类型与参数类型不兼容,则行为未定义。
如果您需要知道是什么导致了程序的某个执行的确切输出,则需要参考该过程中使用的硬件和编译器(以及运行环境,如果适用)的文档。
评论
main()
标题第一:
C 语言中的默认参数升级导致错误......
不,错误不是由“默认参数升级”引起的。
该错误是由于编译器不知道何时调用它而引起的。此时编译器不知道函数预期的参数类型。square
square
今天,这样的代码是非法的(未定义的行为)。
然而,在过去(并非如此)美好的日子里,它被接受(一些编译器仍然这样做)。规则是编译器应该对函数调用中使用的参数执行“默认参数提升”。例如,a 会在调用前变成 a,而 a 会变成 ,等等。float
double
short
int
对于已发布的代码,应应用“默认参数升级”,但它没有效果,因为已经是 .编译器将只生成用于传递 的机器代码,即 照原样。不会发生任何转换。x
double
double
x
当函数读取 时,问题就来了。该函数将假设它被传递了,机器代码将根据“获取 int
参数的规则”读取该值。square
n
int
这就是这部分问题的关键:
...我想知道问题到底是如何发生的吗?
这是关于“调用约定”。一组规则,描述(除其他事项外)如何将参数传递给函数。这些约定不是 C 标准的一部分。调用约定被保留为“实现的东西”。不同的系统可能以不同的方式做到这一点。例如,Windows 和 Linux 使用不同的调用约定。
因此,要找出“问题究竟是如何发生的?”,您需要研究特定系统的调用约定。对此没有单一的解释。
例如,考虑 System V ABI。这里,第一个浮点参数是使用 XMM0 传递的,第一个整数参数是使用 RDI 传递的。因此,对于代码,这意味着调用方将把值存储在 XMM0 中,而被调用方将从 RDI 读取,因为它需要一个整数。显然,这是行不通的。3.0
在您的系统上,可能会发生与上述不同的情况。但常见的问题是一样的:调用方和被调用方对 .double
int
即使某些调用约定指定了相同的“硬件资源”来传递 和 ,也会存在其他低级问题。和的大小可能会有所不同。点值的二进制编码与整数值的二进制编码不同。double
int
double
int
3.0
3
评论
x
double