C 调用隐式声明的函数时出现意外行为
我正在学习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
#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
}