为什么 setjmp() 在 windows 上接受 2 个参数,而在 linux 上只接受 1 个参数?

Why does setjmp() take 2 args on windows but only 1 arg on linux?

setjmp.c 在 Microsoft Visual Studio Professional 2019(版本 16.10.1)

下的 windows 中构建
Rebuild started...
1>------ Rebuild All started: Project: setjmp, Configuration: Release x64 ------
1>setjmp.c
1>C:\Users\john\source\repos\setjmp\setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int
1>Generating code
1>Previous IPDB not found, fall back to full compilation.
1>All 1 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
1>Finished generating code
1>setjmp.vcxproj -> C:\Users\john\source\repos\setjmp\x64\Release\setjmp.exe
1>Done building project "setjmp.vcxproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

实际的cl.exe C编译器命令是

1>    C:\Program Files (x86)\Microsoft Visual Studio19\Professional\VC\Tools\MSVC.29.30037\bin\HostX86\x64\CL.exe /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D NDEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Release\" /Fd"x64\Release\vc142.pdb" /external:env:EXTERNAL_INCLUDE /external:W3 /Gd /TC /FC /errorReport:prompt setjmp.c

源代码为

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

typedef         uint64_t QWORD;
typedef         uint32_t DWORD;
typedef         uint16_t WORD;
typedef         uint8_t  BYTE;

typedef struct MSTJMP_FLOAT128
{
    QWORD Part[2];
} MSTJMP_FLOAT128;

typedef struct JUMP_BUFFER
{
    QWORD Frame0;
    QWORD Rbx0;
    QWORD Rsp0;
    QWORD Rbp0;
    QWORD Rsi0;
    QWORD Rdi0;
    QWORD R120;
    QWORD R130;
    QWORD R140;
    QWORD R150;
    QWORD Rip0;
    DWORD MxCsr4;
    WORD FpCsr2;
    WORD Spare2;
    struct MSTJMP_FLOAT128 Xmm6x;
    struct MSTJMP_FLOAT128 Xmm7x;
    struct MSTJMP_FLOAT128 Xmm8x;
    struct MSTJMP_FLOAT128 Xmm9x;
    struct MSTJMP_FLOAT128 Xmm10x;
    struct MSTJMP_FLOAT128 Xmm11x;
    struct MSTJMP_FLOAT128 Xmm12x;
    struct MSTJMP_FLOAT128 Xmm13x;
    struct MSTJMP_FLOAT128 Xmm14x;
    struct MSTJMP_FLOAT128 Xmm15x;
} JUMP_BUFFER;

int main() {

    struct JUMP_BUFFER jumpbuffer;

   setjmp(&jumpbuffer, 0);

    return 0;
}

setjmp() 调用的反汇编是

setjmp(&jumpbuffer, 0);
00007FF641B51019  xor         edx,edx  
00007FF641B5101B  lea         rcx,[jumpbuffer]  
00007FF641B51020  call        __intrinsic_setjmp (07FF641B51C20h)  

这导致 setjmp() 代码

--- d:\a01\_work\s\src\vctools\crt\vcruntime\src\eh\amd64\setjmp.asm ---------
00007FF82B6F0300  mov         qword ptr [rcx],rdx  
00007FF82B6F0303  mov         qword ptr [rcx+8],rbx  
00007FF82B6F0307  mov         qword ptr [rcx+18h],rbp  

所以一切都正常运行,没有任何错误,因为 windows 在 setjmp.asm

中使用了 C 运行时内部函数

然而,在linux下,它会产生一个错误:

error: macro "setjmp" passed 2 arguments, but takes just 1

Google'ing 找到了这段代码:

#if defined(_WIN64)
/* On w64, setjmp is implemented by _setjmp which needs a second parameter.
 * If this parameter is NULL, longjump does no stack unwinding.
 * That is what we need for QEMU. Passing the value of register rsp (default)
 * lets longjmp try a stack unwinding which will crash with generated code. */
# undef setjmp
# define setjmp(env) _setjmp(env, NULL)
#endif

综上所述,windows可以带2个参数:

setjmp(&jumpbuffer, 0);

和 linux 可以带 1 个参数:

setjmp(&jumpbuffer);

为了验证 linux 喜欢那个 setjmp,我用 gdb 追踪如下:

你的问题是你没有 #include <setjmp.h> 所以编译器不知道函数签名并且 假设 setjmp 是返回 int 的函数并接受未知类型的未知数量的参数。当您声明 func() 时,它是一个在旧 C 标准中返回 int 的函数,您可以调用 func()func(1, 2, 3)func(1, "abc", 'd', 2, 3)... - 都是有效的

注意编译器输出中的警告

setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int

也可以是reproducible on godbolt,MSVC也有同样的警告。你可以看到编译器没有报告任何错误,即使我添加了一些虚拟 setjmp 调用,如

setjmp(&jumpbuffer, 0);
setjmp();
setjmp(1, 2, "abc");
setjmp(&jumpbuffer, 0, 3, 4, 5);

  • Implicit function declarations sometimes work in C?
  • Why does declaration of function with int return type is not compulsory in C?
  • Are prototypes required for all functions in C89, C90 or C99?
  • warning: implicit declaration of function
  • Implicit function declarations in C
  • Is implicit function declaration legal in C89?
  • Is it portable to define and declare the function after calling it in main in C?