【问题标题】:dynamic type variable with void pointer (eventuelly dynamic typecasting) in CC中带有void指针的动态类型变量(最终是动态类型转换)
【发布时间】:2018-06-13 09:44:25
【问题描述】:

我刚刚遇到了动态类型变量的问题(不是真正动态的,但应该在运行时确定),情况是这样的:

我有一个接受双精度数组的函数,将其转换为整数并写入文件,整数可以有不同的位长,如 8、16 和 32。因为它是一个数组,我想使用指向访问最终结果(数组)。所以我现在使用带有 malloc 和 switch case 的 void 指针,但是当我尝试访问或修改这个数组时,需要在每个地方添加 switch case,我的问题是,有没有更好的方法来做到这一点?

当前代码如下:

void foo(double * arr, int len, int iBits, FILE *fh)
{
      void * newArr;
      int iBytePerElement, iBase,i;

      iBytePerElement = iBits / 8;
      iBase = (1 << (iBits - 1)) - 1;

      switch (iBytePerElement)
      {
             case 1:
                  {
                         newArr = (int8_t *) malloc(sizeof(int8_t)*len);
                         break;
                  }
             case 2:
                  {
                         newArr = (int16_t *) malloc(sizeof(int16_t)*len);
                         break;
                  }
             case 4:
                  {
                         newArr = (int32_t *) malloc(sizeof(int32_t)*len);
                         break;
                  }
      }

      for (i = 0; i < len; ++i)
      {
              switch (iBitPerElement)
              {
                     case 1:
                          {
                                ((int8_t *)newArr)[i] = (int8_t)(arr[i]*iBase);
                                 break;
                          }
                     case 2:
                          {
                                ((int16_t *)newArr)[i] = (int16_t)(arr[i]*iBase);
                                 break;
                          }
                     case 4:
                          {
                                ((int32_t *)newArr)[i] = (int32_t)(arr[i]*iBase);
                                 break;
                          }
              }
      }
      fwrite(newArr, iBytePerElement, iBytePerElement*len,fh);
}

【问题讨论】:

  • 函数。制作一组函数来处理您的特殊“通用”数组。然后使用数组的代码只需要使用你的函数,而不必关心它是如何实现的细节。然后,您可以很容易地使其更加通用,以存储任何类型的同类数据(包括结构)。
  • 在不相关的注释中,请记住 malloc 返回 void *,因此不需要演员表(你不应该真的 cast the result of malloc anyway)。
  • 除编外。哥们:可能是,qsort() 可以提供一些启发。它以类似 C 的通用方式实现数组的排序 - 听起来有点类似于您打算做的事情。
  • 如何从这个函数返回newArr?还不清楚为什么在铸造之前需要将浮点数乘以任何东西(iBase?)?这个函数的具体输入输出是什么?
  • 让一个函数做三件不同的事情看起来好像有三个较小的函数试图退出。考虑使用foo8(x,y) 而不是foo(x,y,8)

标签: c void-pointers


【解决方案1】:

对您的示例代码未定义(注释)的地方可能意味着什么做一些假设,我可能会这样做:

#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
void *foo(double * arr, int len, int iBits)  //should return the malloced array not void
{
      void * newArr;
      int iBytePerElement, iBase, i;

      iBytePerElement = iBits / 8;
      iBase = (1 << (iBits - 1)) - 1;

      if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL; //replace the first switch
      //which obviously meant to alloc sizeof(int16_t)*len in the case 2 branch (not (sizeof(int8_t)*len) etc

      for (i = 0; i < len; ++i) {
          switch(iBytePerElement){
          break; case 1: ((int8_t *)newArr)[i] = arr[i]*iBase; 
          break; case 2: ((int16_t *)newArr)[i] = arr[i]*iBase;
          break; case 4: ((int32_t *)newArr)[i] = arr[i]*iBase;
          }
      }
      return newArr;
}

基本上,你只需要第二个开关。

如果你想去掉第二个开关,你可以用一些函数指针替换它。 例如:

#include <stdint.h>
#include <stdlib.h>
#include <limits.h>

#define MK_FN(Bits) \
void to##Bits(void *newArr, double const*arr, int len) \
{ \
    int i; for(i=0; i < len; i++) ((int##Bits##_t *)newArr)[i] = arr[i]*((1<<(Bits-1)-1));  \
} 
MK_FN(8)
MK_FN(16)
MK_FN(32)



void *foo(double * arr, int len, int iBits)  //should return the malloced array not void
{
      void * newArr;
      int iBytePerElement = iBits / 8;

      if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL;
      ((void (*[])(void *,double const*, int)){ [1]=to8, [2]=to16, [4]=to32, })[iBits](newArr,arr,len);
      return newArr;
}

【讨论】:

  • 无论如何,下次请尝试发布定义明确的、可编译的代码。您在可能意味着 sizeof(int16_t)*len 的地方分配 sizeof(int8_t)*len(或者您在第二次切换中超出范围)令人困惑。
  • 感谢回复,是的,这是一个错误(意思是 sizeof(int16_t)*len)。你能解释一下吗,你的第二个代码块做了什么?特别是 ((void ([])(void *,double const, int)){ [1]=to8, [2]=to16, [4]=to32, })[iBits] (newArr,arr,len);让我理解那里发生的事情让我有点困惑,再次感谢
  • MK_FN 使函数 to8to16to32 (##-进行令牌连接,但我认为宏部分应该很清楚;运行 gcc -E 到如果不是,请查看 pp 输出)。这些函数也包含循环,因为由于函数调用开销,保持循环会不必要地减慢速度。现在到奇怪的部分。它可以写成void (*functions[5])(void *,double const*, int) = { [0]=NULL, [1]=to8, [2]=to16, [3]=NULL, [4]=to32 }; (functions[iBits])(newArr,arr,len); 我只是使用复合文字、推断数组大小和隐式归零来使事情变得紧凑。
  • @user152531 第二段确实有点绕,所以我觉得切换更可取。
  • 我在演员阵容上也很轻松。 C 强制转换既强大又危险(为什么 C++ 将它们拆分为 static_cast 和 reinterpret_cast)。最好尽可能避免它们。
猜你喜欢
  • 1970-01-01
  • 2020-03-20
  • 2021-09-20
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
相关资源
最近更新 更多