具有未知类型名称的va_list - 只知道字节大小!

Jan*_*Jan 5 c variadic-functions

我有一个C编程问题:我想编写一个带有变量参数列表的函数,其中每个参数的特定类型都不知道 - 只有它的大小(以字节为单位).这意味着,如果我想获得一个int-Parameter,我(之前的某个地方)定义:将有一个带有sizeof(int)的参数,该参数由回调函数xyz处理.

我的变量参数函数现在应该从其调用中收集所有信息,真正的数据类型特定操作(也可以是用户定义的数据类型)仅通过回调函数进行处理.

在标准的va_arg函数中,不可能说"从我的参数列表中获取X字节的值",所以我想这样做.在这种情况下,我的数据类型是双倍的,但它可以是任何其他数量的字节(甚至是可变的字节).

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

int fn( int anz, ... )
{
        char*   p;
        int             i;
        int             j;
        va_list args;
        char*   val;
        char*   valp;
        int             size = sizeof( double ); 

        va_start( args, anz );

        val = malloc( size );

        for( i = 0; i < anz; i++ )
        {
                memcpy( val, args, size );
                args += size;

                printf( "%lf\n", *( (double*)val ) );
        }

        va_end( args );
}

int main()
{
        fn( 1, (double)234.2 );
        fn( 3, (double)1234.567, (double)8910.111213, (double)1415.161718 );

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

它适用于我,在Linux(gcc)下.但我现在的问题是:这真的很便携吗?或者它会在其他系统和编译器下失败吗?

我的替代方法是取代

                memcpy( val, args, size );
                args += size;
Run Code Online (Sandbox Code Playgroud)

            for( j = 0; j < size; j++ )
                    val[j] = va_arg( args, char );
Run Code Online (Sandbox Code Playgroud)

但后来,我的价值观出错了.

对此有何想法或帮助?

Jos*_*phH 1

它不便于携带,抱歉。va_list 的格式取决于编译器/平台。

您必须使用 va_arg() 来访问 va_list,并且必须将正确的参数类型传递给 va_list。

但是,我相信如果您将正确大小的类型传递给 va_arg,那可能会起作用。IE。类型通常不相关,只与大小相关。然而,即使这样也不能保证在所有系统上都有效。

我想我建议重新审视你的设计,看看你是否能找到替代设计——是否有更多关于你为什么要尝试这样做的细节可以分享?您可以将 va_list 传递给回调吗?

更新

逐字节方法不起作用的原因可能非常复杂。就 C 标准而言,它不起作用的原因是因为它是不允许的 - 您只能使用 va_arg 来访问传递给函数的相同类型。

但我怀疑你想知道幕后发生了什么:)

第一个原因是,当您将“char”传递给函数时,它实际上会自动提升为 int,因此会作为 int 存储到 va_arg 中。因此,当您读取一个字符时,您正在读取一个“int”的内存,而不是一个“char” - 所以您实际上并不是一次读取一个字节。

另一个原因与对齐有关 - 在某些体系结构上(一个例子是最近的 ARM 处理器),“双精度”必须与 64 位(有时甚至是 128 位)边界对齐。也就是说,对于指针值 p,p % 16(p 模 16,以字节为单位 - 即 128 位)必须等于 0。因此,当这些值打包在 va_arg 上时,编译器可能会确保任何 double 值都有空间(填充)添加,因此它们仅在正确对齐的情况下出现 - 但当您一次读取一个字节的条目时,您不会考虑它。

(也可能还有其他原因 - 我对 va_arg 的内部工作不太熟悉。)