c - 可变参数函数传递很长但读取为 va_arg(argList, int)

标签 c gcc 64-bit variadic-functions

我正在将 32 位应用程序转换为 64 位应用程序,我遇到的痛点之一是可变参数函数,它期望一个 long 但可能传递一个整数,例如由于 long 大小的 64 位更改为 64 位,参数被硬编码为 -1 而不是 -1L。拿这个示例代码:

#include <stdio.h>
#include <stdarg.h>

long varargsExample(int input, ...);

int main(int argc, char **argv)
{
    varargsExample(5,
    "TestInt", 0,
    /* This will fail if read as a long */
    "TestIntNegative", -1,
    "TestLong", 0L,
    "TestLongNegative", -1L,
    NULL); 
}

long varargsExample(int firstArg, ...)
{
    va_list args;
    char * name;
    long nextValue;

    va_start(args, firstArg);
    while ((name = va_arg(args, char *)) != 0)
    {
        /* If the type is changed to read in an int instead of long this works */
        nextValue = va_arg(args, long);

        printf("Got [%s] with value [%ld]\n", name, nextValue);

    }
    va_end(args);
    return 0;

}

使用 GCC 64 位编译时运行它会导致:

Got [TestInt] with value [0]
Got [TestIntNegative] with value [4294967295]
Got [TestLong] with value [0]
Got [TestLongNegative] with value [-1]

这是有道理的,因为我猜这被解释为:

0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111

所以填充额外的 32 位来表示 long,我们得到 2^32 - 1 而不是负数。然而,我想知道的是,如果我将 va_arg 读取更改为将值读取为 int,无论是否传递了 int 或 long,这似乎都有效,例如:

nextValue = va_arg(args, int);

这是一个恰巧起作用的 hack,还是 C 规范中的某些东西使它能够始终如一地工作?请注意,此应用程序在 Unix/Linux 和 Windows 上运行,Windows 上的 long 是 32 位,所以我不担心函数被传递的值不能用 32 位整数表示。我创建了一个基本单元测试,它通过 INT_MIN --> INT_MAX 将整数/长整数的混合传递给可变参数函数并读取为 va_arg(args, int) 并且它似乎可以工作(在 AIX、Solaris 和 RHEL 上测试),但我不确定这是否只是在这些平台上发生的未定义行为。

这里的正确解决方法是识别该函数的所有调用者,并确保它们在所有情况下都传递了一个 long,但是如果没有编译器支持来识别这些函数,这些函数的使用相当普遍/很难识别。如果有一个 GCC 扩展我可以利用它来指定自定义可变参数类型检查,类似于格式参数检查(sprintf、printf 等)所做的,我试图将其视为一种替代方案。

最佳答案

编译器执行not know可变参数函数从列表中获取哪些类型,因此它依赖于给定参数的类型。它执行 default argument promotions论据。

对于整数类型,它们基本上将“较小”的类型提升为 intunsigned,并传递 int/unsigned 和“更大”类型不变。

获取参数时为your responsibility从可变参数中获取 正确 类型。其他任何事情都会调用未定义的行为

因此,由于您不传递 long,而是传递 int,因此您必须获取 int。如果两种类型具有相同的表示,则错误可能会被忽视(正如您所怀疑的那样)。

但是,另一种方式 'round 也不应该起作用:如果已推送较大的 long,则采用较小的 int。然而,对于典型的实现,只有在获取下一个参数时才会注意到这一点。无论哪种方式,由于这都是未定义的行为,因此必须避免

gcc 对 printf/scanf-like 格式字符串使用函数 __attribute__s 有一些支持,但作为函数的调用者不提供向调用者提示类型,您在编译器支持方面迷失了(它应该怎么知道?)。

像您提供的功能是骚乱程序的常见来源,最好避免,因为它们很容易出现排版错误,就像您现在注意到的一样。最好将结构数组传递给适当的函数或调用固定参数函数。它们通常是程序员为每一行代码而战的放射性遗产,无论是运行时还是大小。

另一种选择可能是 C11 使用带有 _Generic 的宏为各种参数类型调用固定大小的函数。

关于c - 可变参数函数传递很长但读取为 va_arg(argList, int),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32261736/

相关文章:

c - 指向动态二维数组的动态指针数组?

c - gcc __attribute__((constructor)) 到底什么时候运行?

odbc - Oracle ODBC RowCount 上的 OverFlowException

c - Windows 显示驱动程序 Hook ,64 位

c++ - void* 作为通用引用类型如何工作?

c - 我们如何检查输入字符串是否是有效的 double 值?

c - 显示在给定地址 gdb 找到的值

c - 防止宏的 GCC 警告 "value computed is not used"

未指定类型时 C 编译器不抛出错误

C# 读取进程内存 : How to read a 64 bit memory address?