如何在 C 中将一种类型的数组转换为另一种类型的数组,类型未知,类型大小已知

How to convert an array of one type to an array of another type in C, types are unknown, size of the types is known

提问人:Plegeus 提问时间:7/15/2023 最后编辑:Plegeus 更新时间:7/15/2023 访问量:71

问:

我想知道是否有一种有效的方法可以将 A 类型的数组转换为 C 中的 B 类型的数组,更准确地说,我想填充一个函数,该函数具有两个数组作为输入以及数组中元素的大小。

void populate(void* arr_a, void* arr_b, size_t elsize_a, size_t elsize_b) {
  
  // put elements in arr_b in arr_a

}

如果你知道这些类型,你可以做这样的事情:

 uint32_t* populated = (uint32_t*) arr_a;
 unsigned short* original = (unsigned short*) arr_b;

 for (int i = 0; i < n; i++)
   populated[i] = (uint32_t) original[i];

假设 n 是填充数组和原始数组的大小,并假设两个数组也使用 calloc 进行分配,以便有足够的空间来容纳元素。

所以我的问题是,我是否可以在 C 中模拟或模仿类型参数,以便我可以将一种类型的数组转换为另一种类型的数组(类型将始终是算术类型,如 int、float 等),类型的大小始终是已知的,也就是说,如果arr_a应该是浮点数组, 那么elsize_a将是 4(因为浮点数通常有 4 个字节)。

所以像这样:

uint32_t* populated = ("a pointer of size elsize_a"*) arr_a;

谢谢。

编辑

用例如下:

int* an_array = calloc(n, sizeof(int));
void* bin = some_binary_data_from_a_file();

populate(an_array, bin, sizeof(int), sizeof(short));

但是,如果 bin 包含浮点数,则可以这样做:

populate(an_array, bin, sizeof(int), sizeof(float));

bin 有 n 个算术类型的元素,我想将它们转换为另一种类型,这里是 int。问题在于,bin 可以一次编码为无符号短整型,但另一次可以编码为整数。文件本身有一个标头,说明编码是什么,但假设它始终是相同的类型是不安全的。我想避免这样的情况:

int* arr_a = calloc...;

if (encoding(bin) == INT) {
  int* arr_b = (int*) bin;
  for (int i = 0; i < n; i++)
    arr_a[i] = (int) arr_b[i];
}
if (encoding(bin) == FLOAT) {
  float* arr_b = (float*) bin;
  for (int i = 0; i < n; i++)
    arr_a[i] = (int) arr_b[i];
}

如果需要从 float 到 int 进行转换,可以安全地假设浮点数没有任何小数点。

数组 C 指针 malloc 大小

评论

0赞 user207421 7/15/2023
除非您知道类型,否则不会。否则,您无法区分 和 ,这是非常不同的情况。在 C++ 中非常简单。long->doublefloat->double
0赞 Plegeus 7/15/2023
所以我想我最好的选择是有一些枚举或对象宏,我可以调度(如果 int32...,如果浮点,如果...),然后手动编写类型(在 C 中)。
0赞 chux - Reinstate Monica 7/15/2023
@Plegeus 你怎么能有arrray,却不知道它的类型?你提到了 ,所以听起来你不是在处理数组,而是在处理指针。使用一对 ,这在 C 中看起来是可能的,但需要更多信息。也许发布一个更完整的示例使用?arr_acalloc()_Generic
0赞 Plegeus 7/15/2023
好的,所以,确实arr_a是使用 calloc(n, sizeof(float)) 分配的,所以确实,你知道arr_a是一个浮点数组。但是,我希望填充函数在某种程度上与类型无关,我将更新帖子以澄清情况。

答:

1赞 Eric Postpischil 7/15/2023 #1
  1. 通常,如果没有有关数组元素类型的信息,则不可能将值从源数组复制到目标数组。

证明:对象的类型决定了其值的编码方式(以位为单位),以及表示该值的位的解释方式。例如,对于 32 位二进制整数,值 1 使用位(为简洁起见以十六进制显示)0000000116 进行编码,而对于 IEEE-754 二进制 32 浮点数,该值使用位 3F80000016 进行编码。因此,要知道将位设置为什么以将值 1 放入目标元素中,您必须知道目标元素是否具有类型或其他内容。intfloat

  1. C 标准不提供 所指向的元素的类型信息。 被指定为不完整类型,并且没有标准功能用于查询在分配或先前使用期间与地址关联的类型。void *void

因此,实现一个例程,该例程接受源地址和目标地址,并在这些地址上可能不同的类型之间复制值,唯一的方法是为例程提供类型信息。这可以通过将其硬编码到例程中、创建受支持类型的枚举或其他方法来完成。void *

请注意,仅靠元素大小不足以传达类型信息,例如,两者的大小可能为 4 个字节。intfloat

0赞 0___________ 7/15/2023 #2
  1. 您需要传递有关源和目标类型的信息。
  2. 您需要传递转换函数指针。

例子

typedef enum
{
    type_int,
    type_char, 
    type_short,
    type_double
}types;

size_t getSize(types type)
{
    switch(type)
    {
        case type_int:
        return sizeof(int);
        case type_char:
        return sizeof(char);
        case type_short:
        return sizeof(short);
        case type_double:
        return sizeof(double);
    }
}


void *convert(void *dest, void *src, types srct, types destt, size_t size)
{
    char *cdest = dest;
    char *csrc = src;
    size_t destsize = getSize(destt);
    size_t srcsize = getSize(srct);
    if(destt != srct)
    {
        while(size--)
        {
            if(destt == type_double && srct == type_int)
            {
                int s;
                double d;

                memcpy(&s, csrc, srcsize);
                d = (double)s;
                memcpy(cdest, &d, destsize);
                cdest += destsize;
                csrc += srcsize;
            } else if(destt == type_double && srct == type_char)
            {
                /* do similar for all types pairs */
            }
            /* ... */
         
        }
    }
    else
    {
        memcpy(dest, src, size * destsize);
    }
    return dest;
}
void convfunc(void **dest, void **src)
{
    char *cdest = *dest;
    char *csrc = *src;

    int s;
    double d;

    memcpy(&s, csrc, sizeof(s));
    d = (double)s;
    memcpy(cdest, &d, sizeof(d));
    cdest += sizeof(d);
    csrc += sizeof(s);

    *dest = cdest;
    *src = csrc;
}

void *convert(void *dest, void *src, size_t size, void (*cnv)(void **, void **))
{
    void *wrk = dest;
    while(size--)
    {
        cnv(&dest, &src);
    }
    return wrk;
}