C语言 c - 如何在c中使用void *创建泛型函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13469381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:29:46  来源:igfitidea点击:

How to make generic function using void * in c?

cvoid-pointers

提问by Omkant

I have an incrfunction to increment the value by 1I want to make it generic,because I don't want to make different functions for the same functionality.

我有一个incr函数来增加值,1我想让它通用,因为我不想为相同的功能创建不同的函数。

Suppose I want to increment int,float,charby 1

假设我想增加int, float, charby1

void incr(void *vp)
{
        (*vp)++;
}

But the problem I know is Dereferencing a void pointer is undefined behaviour. Sometimes It may give error :Invalid use of void expression.

但我知道的问题是Dereferencing a void pointer is undefined behaviour。有时它可能会出错:Invalid use of void expression

My mainfunciton is :

我的功能main是:

int main()
{

int i=5;
float f=5.6f;
char c='a';

incr(&i);
incr(&f);
incr(&c);

return 0;
}

The problem is how to solve this ? Is there a way to solve it in Conly

问题是如何解决这个问题?有没有办法C只 解决

or

或者

will I have to define incr()for each datatypes ? if yes, then what's the use of void *

我必须incr()为每个数据类型定义吗?如果是,那有什么用void *

Same problem with the swap()and sort().I want to swap and sort all kinds of data types with same function.

swap()和 的问题相同sort()。我想交换和排序具有相同功能的各种数据类型。

回答by Jerry Coffin

You can implement the first as a macro:

您可以将第一个实现为宏:

#define incr(x) (++(x))

Of course, this can have unpleasant side effects if you're not careful. It's about the only method C provides for applying the same operation to any of a variety of types though. In particular, since the macro is implemented using text substitution, by the time the compiler sees it, you just have the literal code ++whatever;, and it can apply ++properly for the type of item you've provided. With a pointer to void, you don't know much (if anything) about the actual type, so you can't do much direct manipulation on that data).

当然,如果您不小心,这可能会产生令人不快的副作用。不过,这是 C 提供的将相同操作应用于各种类型中的任何一种的唯一方法。特别是,由于宏是使用文本替换实现的,当编译器看到它时,您只有文字代码++whatever;,它可以++正确应用于您提供的项目类型。使用指向 void 的指针,您对实际类型知之甚少(如果有的话),因此您无法对该数据进行太多直接操作)。

void *is normally used when the function in question doesn't really need to know the exact type of the data involved. In some cases (e.g., qsort) it uses a callback function to avoid having to know any details of the data.

void *通常在所讨论的函数并不真正需要知道所涉及数据的确切类型时使用。在某些情况下(例如,qsort)它使用回调函数来避免必须知道数据的任何细节。

Since it does both sort and swap, let's look at qsort in a little more detail. Its signature is:

由于它同时进行排序和交换,让我们更详细地看一下 qsort。它的签名是:

void qsort(void *base, size_t nmemb, size_t size,
           int(*cmp)(void const *, void const *));

So, the first is the void *you asked about -- a pointer to the data to be sorted. The second tells qsort the number of elements in the array. The third, the size of each element in the array. The last is a pointer to a function that can compare individual items, so qsortdoesn't need to know how to do that. For example, somewhere inside qsort will be some code something like:

所以,第一个是void *你问的——指向要排序的数据的指针。第二个告诉 qsort 数组中元素的数量。第三,数组中每个元素的大小。最后一个是指向可以比较单个项目的函数的指针,因此qsort不需要知道如何执行此操作。例如,在 qsort 中的某处将是一些类似的代码:

// if (base[j] < base[i]) ...
if (cmp((char *)base+i, (char *)base+j) == -1)

Likewise, to swap two items, it'll normally have a local array for temporary storage. It'll then copy bytes from array[i]to its temp, then from array[j]to array[i]and finally from tempto array[j]:

同样,要交换两个项目,它通常会有一个本地数组用于临时存储。然后它将字节复制array[i]到它的 temp,然后从array[j]toarray[i]和最后 from tempto array[j]

char temp[size];

memcpy(temp, (char *)base+i, size);              // temp = base[i]
memcpy((char *)base+i, (char *)base+j, size);    // base[i] = base[j]
memcpy((char *)base+j, temp, size);              // base[j] = temp

回答by acjay

Using void *will not give you polymorphic behavior, which is what I think you're looking for. void *simply allows you to bypass the type-checking of heap variables. To achieve actual polymorphic behavior, you will have to pass in the type information as another variable and check for it in your incrfunction, then casting the pointer to the desired type OR by passing in any operations on your data as function pointers (others have mentioned qsortas an example). C does not have automatic polymorphism built in to the language, so it would be on you to simulate it. Behind the scenes, languages that build in polymorphism are doing something just like this behind the scenes.

使用void *不会给你多态行为,这就是我认为你正在寻找的。 void *只是允许您绕过堆变量的类型检查。为了实现实际的多态行为,您必须将类型信息作为另一个变量传递并在您的incr函数中检查它,然后将指针转换为所需的类型或通过将数据上的任何操作作为函数指针传递(其他人已经提到qsort举个例子)。C 语言中没有内置自动多态性,因此您可以对其进行模拟。在幕后,建立在多态中的语言正在幕后做着类似的事情。

To elaborate, void *is a pointer to a generic block of memory, which could be anything: an int, float, string, etc. The length of the block of memory isn't even stored in the pointer, let alone the type of the data. Remember that internally, all data are bits and bytes, and types are really just markers for how the logical data are physically encoded, because intrinsically, bits and bytes are typeless. In C, this information is not stored with variables, so you have to provide it to the compiler yourself, so that it knows whether to apply operations to treat the bit sequences as 2's complement integers, IEEE 754 double-precision floating point, ASCII character data, functions, etc.; these are all specific standards of formats and operations for different types of data. When you cast a void *to a pointer to a specific type, youas the programmer are asserting that the data pointed to actually is of the type you're casting it to. Otherwise, you're probably in for weird behavior.

详细地说,void *是一个指向通用内存块的指针,它可以是任何东西:int、float、string 等。内存块的长度甚至没有存储在指针中,更不用说数据的类型了. 请记住,在内部,所有数据都是位和字节,而类型实际上只是逻辑数据物理编码方式的标记,因为从本质上讲,位和字节是无类型的。在 C 中,此信息不与变量一起存储,因此您必须自己将其提供给编译器,以便它知道是否应用操作将位序列视为 2 的补码整数、IEEE 754 双精度浮点、ASCII 字符数据、功能等;这些都是针对不同类型数据的格式和操作的具体标准。当你投一个void *指向特定类型的指针,作为程序员断言指向的数据实际上是您将其转换为的类型。否则,您可能会出现奇怪的行为。

So what is void *good for? It's good for dealing with blocks of data without regards to type. This is necessary for things like memory allocation, copying, file operations, and passing pointers-to-functions. In almost all cases though, a C programmer abstracts from this low-level representation as much as possible by structuring their data with types, which have built-in operations; or using structs, with operations on these structs defined by the programmer as functions.

那么有什么void *好处呢?它适用于处理数据块而不考虑类型。这对于诸如内存分配、复制、文件操作和传递函数指针之类的事情是必要的。但是,在几乎所有情况下,C 程序员通过使用具有内置操作的类型构造数据来尽可能多地从这种低级表示中抽象出来;或使用结构,对这些结构的操作由程序员定义为函数。

You may want to check out the Wikipedia explanationfor more info.

您可能需要查看维基百科解释以获取更多信息。

回答by caf

You can't do exactly what you're asking - operators like increment need to work with a specific type. So, you coulddo something like this:

你不能完全按照你的要求去做——像 increment 这样的操作符需要使用特定的类型。所以,你可以做这样的事情:

enum type { 
    TYPE_CHAR,
    TYPE_INT,
    TYPE_FLOAT
};

void incr(enum type t, void *vp)
{
    switch (t) {
        case TYPE_CHAR:
        (*(char *)vp)++;
        break;

        case TYPE_INT:
        (*(int *)vp)++;
        break;

        case TYPE_FLOAT:
        (*(float *)vp)++;
        break;
    }
}

Then you'd call it like:

然后你会这样称呼它:

int i=5;
float f=5.6f;
char c='a';

incr(TYPE_INT, &i);
incr(TYPE_FLOAT, &f);
incr(TYPE_CHAR, &c);

Of course, this doesn't really give you anything over just defining separate incr_int(), incr_float()and incr_char()functions - this isn't the purpose of void *.

当然,除了定义单独的incr_int(),incr_float()incr_char()函数之外,这并没有真正给您任何东西- 这不是void *.

The purpose of void *is realised when the algorithm you're writing doesn't care about the real type of the objects. A good example is the standard sorting function qsort(), which is declared as:

的目的void *时,该算法你写不关心现实类型的对象的实现。一个很好的例子是标准排序函数qsort(),它被声明为:

void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));

This can be used to sort arrays of any type of object - the caller just needs to supply a comparison function that can compare two objects.

这可用于对任何类型对象的数组进行排序——调用者只需要提供一个可以比较两个对象的比较函数。

Both your swap()and sort()functions fall into this category. swap()is even easier - the algorithm doesn't need to know anything other than the size of the objects to swap them:

您的swap()sort()函数都属于这一类。 swap()甚至更容易 - 算法不需要知道交换对象的大小以外的任何东西:

void swap(void *a, void *b, size_t size)
{
    unsigned char *ap = a;
    unsigned char *bp = b;
    size_t i;

    for (i = 0; i < size; i++) {
        unsigned char tmp = ap[i];

        ap[i] = bp[i];
        bp[i] = tmp;
    }
}

Now given any array you can swap two items in that array:

现在给定任何数组,您可以交换该数组中的两个项目:

int ai[];
double ad[];

swap(&ai[x], &ai[y], sizeof(int));
swap(&di[x], &di[y], sizeof(double));

回答by Maroun

Example for using "Generic" swap.

使用“通用”交换的示例。

This code swaps two blocks of memory.

此代码交换两个内存块。

void memswap_arr(void* p1, void* p2, size_t size)
{      
      size_t         i;
      char* pc1= (char*)p1;
      char* pc2= (char*)p2;
      char  ch;

      for (i= 0; i<size; ++i) {
        ch=     pc1[i];
        pc1[i]= pc2[i];
        pc2[i]= ch;
      }
}

And you call it like this:

你这样称呼它:

int main() {
     int i1,i2;
     double d1,d2;
     i1= 10; i2= 20;
     d1= 1.12; d2= 2.23;
     memswap_arr(&i1,&i2,sizeof(int));     //I use memswap_arr to swap two integers
     printf("i1==%d i2==%d \n",i1,i2);     //I use the SAME function to swap two doubles
     memswap_arr(&d1,&d2,sizeof(double));      
     printf("d1==%f d2==%f \n",d1,d2);
     return 0;
}

I think that this should give you an idea of how to use one function for different data types.

我认为这应该让您了解如何将一个函数用于不同的数据类型。

回答by Maciej

You should cast your pointer to concrete type before dereferencing it. So you should also add code to pass what is the type of pointer variable.

在取消引用它之前,您应该将指针转换为具体类型。所以你还应该添加代码来传递指针变量的类型。

回答by Saeed Baig

Sorry if this may come off as a non-answer to the broad question "How to make generic function using void * in c?".. but the problems you seem to have (incrementing a variable of an arbitrary type, and swapping 2 variables of unknown types) can be much easier done with macros than functions and pointers to void.

抱歉,如果这可能不是对广泛问题“如何在 c 中使用 void * 创建泛型函数?”的非答案,但您似乎遇到的问题(增加任意类型的变量,并交换 2 个变量)未知类型)可以比函数和指向 void 的指针更容易使用宏来完成。

Incrementing's simple enough:

递增很简单:

#define increment(x) ((x)++)

For swapping, I'd do something like this:

对于交换,我会做这样的事情:

#define swap(x, y)                  \
({                                  \
        typeof(x) tmp = (x);        \
        (x) = (y);                  \
        (y) = tmp;                  \
})

...which works for ints, doubles and char pointers (strings), based on my testing.

...根据我的测试,它适用于整数、双精度数和字符指针(字符串)。

Whilst the incrementing macro should be pretty safe, the swap macro relies on the typeof()operator, which is a GCC/clang extension, NOT part of standard C (tho if you only really ever compile with gcc or clang, this shouldn't be too much of a problem).

虽然递增宏应该非常安全,但交换宏依赖于typeof()操作符,它是 GCC/clang 扩展,不是标准 C 的一部分(如果你真的只用 gcc 或 clang 编译,这不应该太多的问题)。

I know that kind of dodged the original question; but hopefully it still solves your original problems.

我知道那种回避了最初的问题;但希望它仍然可以解决您原来的问题。

回答by jlaraval

You can use the type-generic facilities (C11 standard). If you intend to use more advanced math functions (more advanced than the ++operator), you can go to <tgmath.h>, which is type-generic definitions of the functions in <math.h>and <complex.h>.

您可以使用类型通用设施(C11 标准)。如果你打算使用更先进的数学函数(而不是更先进的++运营商),你可以去<tgmath.h>,这是功能型通用定义,<math.h><complex.h>

You can also use the _Generickeyword to define a type-generic function as a macro. Below an example:

您还可以使用_Generic关键字将类型泛型函数定义为宏。下面是一个例子:

#include <stdio.h>

#define add1(x) _Generic((x), int: ++(x), float: ++(x), char: ++(x), default: ++(x))

int main(){
  int i = 0;
  float f = 0;
  char c = 0;

  add1(i);
  add1(f);
  add1(c);

  printf("i = %d\tf = %g\tc = %d", i, f, c);
}

You can find more information on the language standardand more soffisticated examples in this post from Rob's programming blog.

您可以在Rob 的编程博客的这篇文章中找到有关语言标准的更多信息和更复杂的示例。

As for the * void, swap and sort questions, better refer to Jerry Coffin's answer.

至于* void,swap 和 sort 的问题,最好参考Jerry Coffin的回答。