C中的默认参数提升导致错误,但我不知道确切原因

sc4hvdpw  于 2023-11-16  发布在  其他
关注(0)|答案(2)|浏览(105)

下面的代码示例来自K. N. King的 C Programming. A Modern Approach。它不能正常工作:输出是1而不是9。作者说这是默认参数提升引起的问题:
在调用square时,编译器还没有看到原型,所以它不知道square需要一个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;
}

字符串
我运行了程序,它不工作,但我想知道到底是什么问题在运行时导致它。

sd2nnvve

sd2nnvve1#

标题第一:
C中的默认参数提升导致错误....
不,错误不是由“default argument promotion”引起的。
该错误是由于编译器在调用square时缺乏对它的了解而导致的。当时编译器不知道函数square所期望的参数类型。
今天这样的代码是非法的(未定义的行为)。
然而,在过去(不那么好)的日子里,它被接受(一些编译器仍然这样做)。规则是编译器应该对函数调用中使用的参数进行“默认参数提升”。例如,float将在调用之前转换为double,而short将转换为int,等等。
对于发布的代码,应该应用“default argument promotion”,但它没有效果,因为x已经是double。编译器将只生成机器码来传递double,即x。不会发生转换。
当函数square读取n时,问题就来了。函数将假设它被传递了一个int,机器码将根据“获取int参数的规则"读取值。
这是这部分问题的关键
我想知道这个问题到底是怎么发生的。
它是关于“调用约定”的。一组规则描述(除其他事项外)如何将参数传递给函数。这些约定不是C标准的一部分。调用约定被保留为“实现的事情”。不同的系统可能会以不同的方式完成它。例如Windows和Linux使用不同的调用约定。
因此,要找出“问题究竟是如何发生的?”你需要研究特定系统的调用约定。
以System V ABI为例。这里第一个浮点参数使用XMM 0传递,第一个整数参数使用RDI传递。因此,对于您的代码,这意味着调用方将在XMM 0中存储值3.0,而被调用方将从RDI读取,因为它需要一个整数。显然,这是行不通的。
在您的系统上,可能会发生与上述不同的情况,但常见的问题是相同的:调用方和被调用方期望的是不同的东西,doubleint
即使某些调用约定指定了相同的“HW资源”来传递doubleint,也会有其他低级问题。doubleint的大小可能不同。浮点值3.0的二进制编码与整数值3的二进制编码不同。

5lhxktic

5lhxktic2#

首先,问题中的代码不是有效的C99,更不用说C11,C18或C23了。在C99和更高版本中,函数必须在使用前声明(main()除外)。只有C90允许函数在没有声明的情况下使用-但这不是一个标准,目前已经过时。您必须在调用之前声明函数。
也就是说,给定代码片段中的另一个问题正是引用中提到的,当需要int时,您发送了double值。由于doubleint兼容的类型,因此该程序调用了未定义的行为。输出无法以任何方式调整。
引用C标准,章节6.5.2.2/P6

  • .如果函数定义的类型包含原型,并且原型以省略号(,.)结尾,或者提升后的参数类型与参数类型不兼容,则行为未定义。*

如果你需要知道是什么导致了程序的特定执行的 * 确切 * 输出,你需要参考硬件和编译器(以及运行环境,如果适用)的文档。

相关问题