传递一个 void* 指针,然后在 C 中进行转换

Passing a void* pointer and then cast in C

我正在传递一个指向我的函数的指针,但是这个指针的类型可能会改变。我不想编写此函数的多个版本,例如 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

如有任何帮助,我们将不胜感激。

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

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_floatfunc_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);
    }
}

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

此外,您不能对空指针进行算术运算。 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 个版本中的哪一个之前应该三思。

实际上,您是在尝试使 pIn 变量的 type 不同,具体取决于传入的值 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”和“将函数应用于 float”函数,并分别指定实际的工作函数,并且准确无误一次。下面是两个“应用函数”函数:

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;
}

注意这个函数你只需要写一次。它接受 returns 和 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");