【问题标题】:Passing a void* pointer and then cast in C传递一个 void* 指针,然后在 C 中强制转换
【发布时间】:2021-10-26 10:12:49
【问题描述】:

我正在传递一个指向我的函数的指针,但是这个指针的类型可能会改变。我不想写这个函数的几个版本,比如func_float()和func_int()。所以我将void* 的指针传递给我的函数,然后将类型转换为int* 或float*,这取决于参数type。这是我的代码:

void func(void* pSrc, float* pDst, int len, int type)
{
    if (type == 0)
        float* pIn = (float*)pSrc;
    else
        int* pIn = (int*)pSrc;
    float* pOut = pDst;
    while (len--)
        *pOut++ = (float)*pIn++ * 2.0;  // cast to float and then do some math
}

int main()
{
    int input[3] = { 1,2,3 };
    float output[3] = { 0 };
    func(input, output, 3, 1);
    return 0;
}

但这似乎行不通。 VS 告诉我a dependent statement may not be a declaration。我试图在if 之前将pIn 声明为void*,但是当我取消引用指针pIn 时出现另一个错误expression must be a pointer to a complete object type。

任何帮助将不胜感激。

【问题讨论】:

  • pIn 的声明是 if 分支和 else 分支的本地声明,它们在 if/else 之后不存在。您必须在两个分支中的每一个中复制算法。
  • 一种方法是创建一个宏来定义您的函数,并将类型作为宏参数(并粘贴标记,因此函数具有唯一的名称
  • 在这种特殊情况下,只写 2 个函数会更容易
  • 我收到很多其他编译器错误。值得注意的是,您的函数参数类型错误,并且您传递的参数数量不正确。
  • 请考虑 (float) 根据 *pIn++ 是浮点数还是整数,所做的事情完全不同。机器代码会是什么样子?编译器必须编写两个不同的函数,一个用于浮点,一个用于 int!

标签: c void-pointers


【解决方案1】:

这里有很多问题。错误的参数数量,错误的类型,错误的变量范围 - 非常基本的东西。

此外,您不能对 void 指针进行算术运算。 gcc 将其作为非标准扩展提供,但 my_void_pointer++ 并不意味着“增加 1 个项目”而是“增加 1 个字节”,类似于字符类型指针。

由于 void 指针不包含任何类型信息,编译器无法知道指向的项目包含多少字节。我建议简单地忘记 void 指针算术和 gcc 扩展。

要回答如何在 C 中执行此操作的问题,我可以想到 3 种不同的方法:


KISS principle/常识解决方案是简单地避免 C 中的类型泛型编程 - 它的用处被总体高估了 - 并编写两个单独的函数:

#include <stdio.h>

void func_int (int* dst, const int* src, size_t size)
{
  for(size_t i=0; i<size; i++)
  {
    dst[i] = src[i] * 2;
  }
}

void func_float (float* dst, const float* src, size_t size)
{
  for(size_t i=0; i<size; i++)
  {
    dst[i] = src[i] * 2;
  }
}

int main (void)
{
    int input_i[3] = { 1,2,3 };
    int output_i[3];
    func_int(output_i, input_i, 3);
    for(size_t i=0; i<3; i++)
    {
      printf("%d ", output_i[i]);
    }
    
    puts("");

    float input_f[3] = { 1.0f, 2.0f, 3.0f };
    float output_f[3];
    func_float(output_f, input_f, 3);
    for(size_t i=0; i<3; i++)
    {
      printf("%f ", output_f[i]);
    }
}

如果你仍然坚持类型泛型编程(“为了复杂而保持复杂”),那么你可以使用标准 C _Generic:

#define func(dst,src,size) _Generic((dst), int*: func_int, float*: func_float) (dst,src,size)

func(output_i, input_i, 3);
func(output_f, input_f, 3);

最后你可以以同样的方式进行“模板元编程”,通过使用_Generic 和 X-macros 的组合来生成函数本身(“让它保持愚蠢的复杂”)

#include <stdio.h>

#define SUPPORTED_TYPES(X) \
  X(int)                   \
  X(float)                 \

#define FUNC_DEFINITIONS(type)                              \
void func_##type (type* dst, const type* src, size_t size)  \
{                                                           \
  for(size_t i=0; i<size; i++)                              \
  {                                                         \
    dst[i] = src[i] * 2;                                    \
  }                                                         \
}
SUPPORTED_TYPES(FUNC_DEFINITIONS)

#define GENERIC_LIST(type) ,type*: func_##type
#define func(dst,src,size) _Generic((dst) SUPPORTED_TYPES(GENERIC_LIST)) (dst,src,size)

int main (void)
{
    int input_i[3] = { 1,2,3 };
    int output_i[3];
    func(output_i, input_i, 3);
    for(size_t i=0; i<3; i++)
    {
      printf("%d ", output_i[i]);
    }
    
    puts("");

    float input_f[3] = { 1.0f, 2.0f, 3.0f };
    float output_f[3];
    func(output_f, input_f, 3);
    for(size_t i=0; i<3; i++)
    {
      printf("%f ", output_f[i]);
    }
}

总体而言,优秀的程序员会保持代码简单,而糟糕的程序员会尽量编写复杂的代码。因此,在决定使用这 3 个版本中的哪一个之前,您应该三思而后行。

【讨论】:

  • 最初我写了两个函数,但我想知道是否有更好的方法。你们都强烈推荐两个单独的函数,所以我会这样做。谢谢!
  • KISS 是一个很好的原则——但是,DRY 也是如此。有时很难知道支持哪种原则。
  • @SteveSummit DRY 在许多情况下都有意义,当然,但它也很危险,而且往往弊大于利。就像在我的 X 宏示例中,当有人决定将他们的程序混淆为遗忘只是为了节省一点重复。或者对于无数其他示例,只需看看 C++ 语言的持续破坏。由于复杂性增加而引入错误的机会通常大于重复引起的错误变化。
  • @Lundin 这就是我所说的“有时很难知道支持哪种原则”。
【解决方案2】:

实际上,您正在尝试使pIn 变量的类型 不同,具体取决于传入的type 标志的值。但没有办法做到这一点。每个变量必须只有一种类型,在编译时就知道了。

你可以达到的最接近你想要做的事情可能是这样的:

void func(void *pSrc, float *pDst, int len, int type)
{
    char *pIn = pSrc;        /* char * so can do pointer arithmetic */
    float *pOut = pDst;
    while (len--) {
        *pOut++ = (type == 0 ? *(float *)pIn : *(int *)pIn) * 2.0;

        if (type == 0)
             pIn += sizeof(float);
        else pIn += sizeof(int);
    }
}

然而,这有点丑陋。如果您的实际func 更复杂,那可能是值得的。但是,如果您所做的只是乘以 2,那么硬着头皮使用两个单独的函数可能会更干净。 (对不起,我知道你说过这不是你想做的。)

子表达式(type == 0 ? *(float *)pIn : *(int *)pIn) 有点像我写的那样令人震惊。 (C 因允许这种简洁性而备受赞誉,也臭名昭著。)您可能更喜欢以更“通俗易懂”的方式写东西:

void func(void *pSrc, float *pDst, int len, int type)
{
    char *pIn = pSrc;        /* char * so can do pointer arithmetic */
    float *pOut = pDst;
    while (len--) {
        double inVal;

        if (type == 0) {
            inVal = *(float *)pIn;
            pIn += sizeof(float);
        } else {
            inVal = *(int *)pIn;
            pIn += sizeof(int);
        }

        *pOut++ = inVal * 2.0;
    }
}

如果您对type 有更多不同的值,或者如果您使用inVal 做更多涉及的事情,那么第二个公式也会使事情变得更容易和更清晰。


附录:这是一种完全不同的方法。在您最初的问题陈述中,您有一个int 数组或float 数组,并且您希望将每个元素乘以2,而无需重复太多代码。我假设在您的实际情况下,您可以应用比“乘以 2”更多的功能。

如果您习惯使用函数指针,您可以直接编写一对“将函数应用于 int”和“将函数应用于浮点”函数,分别指定实际的工作函数,并且只指定一次。下面是两个“应用函数”函数:

void apply_func_to_int(double (*func)(double), int *pSrc, float *pDst, int len)
{
    for(int i = 0; i < len; i++)
        pDst[i] = func(pSrc[i]);
}

void apply_func_to_float(double (*func)(double), float *pSrc, float *pDst, int len)
{
    for(int i = 0; i < len; i++)
        pDst[i] = func(pSrc[i]);
}

如您所见,它们彼此非常非常相似——基本上是复制粘贴的工作——但它们都很短,重复并不太令人反感。

这是要应用的函数:

double func(double x)
{
    return x * 2;
}

请注意,您只需编写一次此函数。它接受并返回double,以实现相当全面的通用性。

把它们放在一起:

int main()
{
    int i;
    int input[3] = { 1, 2, 3 };
    float output[3] = { 0 };
    apply_func_to_int(func, input, output, 3);
    for(i = 0; i < 3; i++) printf("%f ", output[i]); printf("\n");

    float input2[3] = { 1.1, 2.2, 3.3 };
    apply_func_to_float(func, input2, output, 3);
    for(i = 0; i < 3; i++) printf("%f ", output[i]); printf("\n");
}

当然你也可以应用其他功能:

#include <math.h>

/* ... */

    float input3[4] = { 2, 10, 25, 1.44 };
    apply_func_to_float(sqrt, input3, output, 4);
    for(i = 0; i < 4; i++) printf("%f ", output[i]); printf("\n");

【讨论】:

  • 我想这就是我想要的。谢谢先生。
  • 查看更新以获得完全不同的方法。
  • 对不起,乘以 2 只是一个例子。其实很复杂。
【解决方案3】:

变量在其作用域结束时不再存在。你必须这样做:

void func(void* pSrc, float* pDst, int len, int type)
{
    if (type == 0)
        func_float(pSrc, pDst, len);
    else
        func_int(pSrc, pDst, len);
}

然后实现func_float和func_int

如果您想支持多种类型,这可能会很好:

void foo(void* pSrc, float* pDst, int len, int type) 
{
    switch(type) {
        case 0: func_float(pSrc, pDst, len); break;
        case 1: func_int(pSrc, pDst, len); break;
        // More cases:
        default: perror("Invalid type"); exit(EXIT_FAILURE);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    • 2018-03-05
    相关资源
    最近更新 更多