传递一个 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_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);
}
}
这里有很多问题。参数数量错误、使用的类型错误、变量范围错误 - 非常基础的东西。
此外,您不能对空指针进行算术运算。 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");
我正在传递一个指向我的函数的指针,但是这个指针的类型可能会改变。我不想编写此函数的多个版本,例如 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_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);
}
}
这里有很多问题。参数数量错误、使用的类型错误、变量范围错误 - 非常基础的东西。
此外,您不能对空指针进行算术运算。 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");