微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

va_arg 如何在可变参数函数中实际工作?

如何解决va_arg 如何在可变参数函数中实际工作?

我阅读了可变参数函数以及数据类型和宏如何工作,我以为我了解它是如何工作的,但我发现有些不同,这是我一直在试验的内容,我对不同宏和数据的理解输入工作:

typedef char * va_list;
#define va_start( ap,v ) ap = (char *)&v + sizeof( v )
#define va_arg( ap,t ) ( (t *) ( ap += sizeof( t ) )[-1]
#define va_end( ap ) ap = NULL

所以这只是一个指针,它一次移动 1 个字节并移动 t 数据类型使用的字节数,所以我试图在不使用此宏或 <stdarg.h> 的情况下复制行为所有,只是为了更好地理解它是如何工作的,使用标头的函数代码是这样的:

int _print_ints(int n,...)
{
    va_list listArgs;

    va_start( listArgs,n );

    for(int i = 0; i < n; i++)
    {
        printf("%3d ",va_arg( listArgs,int ) );
    }

    va_end( listArgs );
}

它按预期工作,我制作的代码是这样的:

int print_ints(int n,...)
{
    char *arg = (char *)&n;

    for(int i = 0; i < n; i++)
    {
        int val = *( (int *) (arg += sizeof( int ) * 2 ) );
        printf("%3d ",val);
    }

    arg = NULL;
}

这个也按预期工作,但是,你看到我必须移动两倍于我必须首先移动的假设大小,而不是移动每个变量的 4 bytes (我系统中的 int 大小),我必须移动 8 bytes 这是一个长长的大小,代码中的常量变量是 int 所以我真的不明白为什么在堆栈中为了从一个变量移动到另一个我必须移动两次它的大小,这里是完整的代码

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

int _print_ints(int n,int ) );
    }

    va_end( listArgs );
}

int print_ints(int n,val);
    }

    arg = NULL;
}

int main( void )
{
    _print_ints(5,1,2,3,4,5);
    printf("\n");
    print_ints(5,6,7,8,9,10);

    return EXIT_SUCCESS;
}

输出

1  2  3  4  5
6  7  8  9 10

总结一下,为什么我必须移动 sizeof( int ) * 2 而不是 sizeof( int )

可能是我得到信息的那本书已经过时了

解决方法

首先,我不确定您是否直接从系统的 stdarg.h 标头中获取了这些宏,或者您只是认为编译器正在使用这些宏,因为它可能非常好吧不是。使用 -E -P 编译您的源代码以仅应用预处理器并查看预处理后 _print_ints() 的样子。在现代编译器上,va_* 宏只是编译器内置的(您将在输出中看到类似 __builtin_va_arg(...) 的内容),因此没有 C 等效项,只有编译器知道它们是如何实现的以及所需的代码对于那些。

现在,如果您展示的那些确实是您系统上正在使用的宏,那么您对 ​​va_start()va_arg() 的重新实现就是错误的:

char *arg = (char *)&n;             // wrong
char *arg = (char *)&n + sizeof(n); // correct

int val = *( (int *) (arg += sizeof( int ) * 2 ) ); // wrong
int val = *((int *)(arg += sizeof(int)) - 1);       // correct

但是,由于您正在执行 += sizeof(int) * 2 并且奇怪地获得了正确的结果,这可能意味着您的编译器确实在一个不同的中实现了 va_start()va_arg() > 比您想象的要好,很可能将您的 4 字节整数与堆栈上的 8 字节边界对齐(如果您使用的是 64 位系统,则可能有意义)。

在任何情况下,您很可能只是靠运气得到正确的结果,因为您正在使用野指针,因此如果您不小心,您所做的事情很容易最终成为未定义的行为。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。