Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 调用隐式声明的函数时出现意外行为_C - Fatal编程技术网

C 调用隐式声明的函数时出现意外行为

C 调用隐式声明的函数时出现意外行为,c,C,我正在学习C语言,并编写了以下代码: #include <stdio.h> int main() { double a = 2.5; say(a); } void say(int num) { printf("%u\n", num); } 意外运行该程序会导致打印1。从我有限的理解来看,因为我没有为编译器添加函数原型,编译器在第6行的函数调用中隐式地创建了一个原型,期望一个double作为参数,并警告我这个隐式声明。但是后来我用int

我正在学习C语言,并编写了以下代码:

#include <stdio.h>

int main()
{
    double a = 2.5;
    say(a);
}

void say(int num)
{
    printf("%u\n", num);
}
意外运行该程序会导致打印1。从我有限的理解来看,因为我没有为编译器添加函数原型,编译器在第6行的函数调用中隐式地创建了一个原型,期望一个double作为参数,并警告我这个隐式声明。但是后来我用int类型的参数定义了这个函数。编译器给了我两个关于类型不匹配的警告

我期望参数强制,这意味着double将被转换为整数。但是在这种情况下,输出应该是2,而不是1。这到底是怎么回事

我期待辩论的强制

如果您正确地声明了该函数,您将得到这样的结果。但是正如您正确指出的那样,您没有声明函数,而是得到了一个隐式声明,该声明将一个
double
作为参数。因此,当编译器看到函数调用时,它会看到一个函数调用,其中参数为double,函数为double。因此,它没有理由强迫任何事情。它只是生成调用一个以double作为参数的函数的常用代码

这到底是怎么回事

就C语言而言,它是未定义的行为,仅此而已

在实现方面,正如我所说,可能发生的是,编译器将生成用于调用具有双精度的函数的常规代码。在使用常用调用约定的64位x86体系结构上,这意味着将值2.5放入XMM0寄存器,然后调用函数。函数本身将假定参数为
int
,因此它将从EDI寄存器(或使用Microsoft调用约定的ECX)读取其值,EDI寄存器是用于传递第一个整数参数的寄存器。所以参数被写入一个寄存器,然后从一个完全不同的寄存器读取,所以你会得到该寄存器中发生的任何事情

然而,究竟什么才是[未定义的行为]

您(隐式地)使用一种类型声明函数,但随后使用另一种类型定义它。如果函数的声明和定义不匹配,则会导致未定义的行为

我期待辩论的强制

如果您正确地声明了该函数,您将得到这样的结果。但是正如您正确指出的那样,您没有声明函数,而是得到了一个隐式声明,该声明将一个
double
作为参数。因此,当编译器看到函数调用时,它会看到一个函数调用,其中参数为double,函数为double。因此,它没有理由强迫任何事情。它只是生成调用一个以double作为参数的函数的常用代码

这到底是怎么回事

就C语言而言,它是未定义的行为,仅此而已

在实现方面,正如我所说,可能发生的是,编译器将生成用于调用具有双精度的函数的常规代码。在使用常用调用约定的64位x86体系结构上,这意味着将值2.5放入XMM0寄存器,然后调用函数。函数本身将假定参数为
int
,因此它将从EDI寄存器(或使用Microsoft调用约定的ECX)读取其值,EDI寄存器是用于传递第一个整数参数的寄存器。所以参数被写入一个寄存器,然后从一个完全不同的寄存器读取,所以你会得到该寄存器中发生的任何事情

然而,究竟什么才是[未定义的行为]

您(隐式地)使用一种类型声明函数,但随后使用另一种类型定义它。如果函数的声明和定义不匹配,则会导致未定义的行为

这到底是怎么回事

从C标准的角度来看,它是未定义的行为

这到底是怎么回事

我假设您使用的是
x86\u 64
体系结构。该标准定义了如何将变量传递给该体系结构上的函数
double
参数通过
%xmm0
寄存器传递,而
edi
寄存器用于将第一个参数传递给函数

:

main
%xmm0
的值设置为
double
。然而,
say()
读取代码中未设置的
%edi
寄存器。由于
edi
中有一些剩余值
1
,很可能来自crt0或类似的值,因此您可以对
1
进行编码打印

@编辑剩余值实际上来自
main
参数。它是
int main(int argc,char*argv[])
-因为您的程序没有传递任何参数,
argc
被启动代码设置为
1
,这意味着
%edi
中的剩余值是
1

例如,您可以在调用say之前,通过调用一个接受
int
的函数,将
%edi
值“手动”设置为某个值。下面的代码打印我在
func()
call中输入的值

int func(int a) {}    
int main() {
    func(50); // set %edi to 50
    double a = 2.5;
    say(a);
}
void say(int num) {
    printf("%u\n", num); // will print '50', the leftover in %edi
}
这到底是怎么回事

从C标准的角度来看,它是未定义的行为

这到底是怎么回事

我假设您使用的是
x86\u 64
体系结构。该标准定义了如何将变量传递给该体系结构上的函数
double
参数通过
%xmm0
寄存器传递,而
edi
寄存器用于将第一个参数传递给函数

:

main
%xmm0
的值设置为
double
。然而,
say()
读取代码中未设置的
%edi
寄存器。因为
edi
中有一些剩余值
1
,很可能来自
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        movsd   xmm0, QWORD PTR .LC0[rip]
        movsd   QWORD PTR [rbp-8], xmm0
        mov     rax, QWORD PTR [rbp-8]
        movq    xmm0, rax           ; set xmm0 to the value of double
        mov     eax, 1              ; I guess gcc assumes `int say(double, ...)` for safety
        call    say
        mov     eax, 0
        leave
        ret
say:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi   ; read %edi
        mov     eax, DWORD PTR [rbp-4]   
        mov     esi, eax                 ; pass value in %edi as argument to printf
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
        nop
        leave
        ret
int func(int a) {}    
int main() {
    func(50); // set %edi to 50
    double a = 2.5;
    say(a);
}
void say(int num) {
    printf("%u\n", num); // will print '50', the leftover in %edi
}