将包含 32 位值的 uint64_t 传递给参数实际上是 uint32_t 的外部函数是否安全?

Is it safe to pass a uint64_t containing a 32-bit value to an external function whose parameter is actually a uint32_t?

我正在开发一个跨平台程序,该程序使用 C 链接从动态库调用函数。我需要支持这个动态库的多个版本,但是在我需要支持的两个版本之间,有一个函数参数从uint32_t变成了uint64_t

如果我向此函数传递一个 uint64_t,其中包含的值仍可表示为 uint32_t,即使函数的参数实际上是 uint32_t,这样做是否安全?

更具体地说:

如果编译成动态库的函数源为:

extern "C" void foo(uint32_t param) {
    ...
}

我这样使用函数安全吗:

extern "C" void foo(uint64_t);

uint32_t value32 = 10; // Ensure value can be represented by uint32_t
uint64_t value64 = value32;
foo(value64);

如果是,在不同平台上这样做是否安全?我的这个程序支持 32 位和 64 位 Windows(两者都编译为 x86)、x86_64 macOS、arm64 macOS、x86 Linux 和 x86_64 Linux.

我敢肯定有人会输入一些人为的系统示例,但在 Intel 系统上,请记住较大的寄存器构建在较小的寄存器之上(rax eax,它建立在 axal) 之上,因此您尝试做的事情会起作用。

不,这是非法的。 C++20 [basic.link] p11:

After all adjustments of types (during which typedefs (9.2.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical.

此外,它实际上会在使用通常的 stack-based 调用约定的 32 位 x86 系统上失败。用 uint32_t 定义的函数将在堆栈中查找一个双字,但两个双字将被压入。它上面的任何参数都会出现在错误的位置。对于 stdcall 约定,情况更糟,在该约定中,被调用函数会弹出自己的参数;它会弹出错误的数量,使堆栈不平衡,并在返回后造成各种混乱。