• c语言void+c语言泛型设计


    转载:https://www.runoob.com/w3cnote/c-void-intro.html

    C 语言中 void* 详解及应用

    void 在英文中作为名词的解释为 "空虚、空间、空隙",而在 C 语言中,void 被翻译为"无类型",相应的void * 为"无类型指针"

    void 似乎只有"注释"和限制程序的作用,当然,这里的"注释"不是为我们人提供注释,而是为编译器提供一种所谓的注释。


    void 的作用

    1.对函数返回的限定,这种情况我们比较常见。

    2.对函数参数的限定,这种情况也是比较常见的。

    一般我们常见的就是这两种情况:

    • 当函数不需要返回值值时,必须使用void限定,这就是我们所说的第一种情况。例如:void func(int a,char *b)。
    • 当函数不允许接受参数时,必须使用void限定,这就是我们所说的第二种情况。例如:int func(void)。

    void 指针的使用规则

    1. void 指针可以指向任意类型的数据,就是说可以用任意类型的指针对 void 指针对 void 指针赋值。例如:

    int *a
    void *p
    p=a

    如果要将 void 指针 p 赋给其他类型的指针,则需要强制类型转换,就本例而言:a=(int *)p。在内存的分配中我们可以见到 void 指针使用:内存分配函数 malloc 函数返回的指针就是 void * 型,用户在使用这个指针的时候,要进行强制类型转换,也就是显式说明该指针指向的内存中是存放的什么类型的数据 (int *)malloc(1024) 表示强制规定 malloc 返回的 void* 指针指向的内存中存放的是一个个的 int 型数据。

    2. 在 ANSI C 标准中,不允许对 void 指针进行一些算术运算如 p++ 或 p+=1 等,因为既然 void 是无类型,那么每次算术运算我们就不知道该操作几个字节,例如 char 型操作 sizeof(char) 字节,而 int 则要操作 sizeof(int) 字节。而在 GNU 中则允许,因为在默认情况下,GNU 认为 void * 和 char * 一样,既然是确定的,当然可以进行一些算术操作,在这里sizeof(*p)==sizeof(char)。

    void 几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个 void 变量,让我们试着来定义:

    void a;

    这行语句编译时会出错,提示"illegal use of type 'void'"。即使 void a 的编译不会出错,它也没有任何实际意义。

    众所周知,如果指针 p1 和 p2 的类型相同,那么我们可以直接在 p1 和 p2 间互相赋值;如果 p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。

    float *p1;
    int *p2;
    p1 = p2;
    //其中p1 = p2语句会编译出错,
    //提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:
    p1 = (float *)p2;

    而 void * 则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换。

    void *p1;
    int *p2;
    p1 = p2;

    但这并不意味着,void * 也可以无需强制类型转换地赋给其它类型的指针。因为"无类型"可以包容"有类型",而"有类型"则不能包容"无类型"。

    小心使用 void 指针类型:

    按照 ANSI(American National Standards Institute) 标准,不能对 void 指针进行算法操作,即下列操作都是不合法的:

    void * pvoid;
    pvoid++; //ANSI:错误
    pvoid += 1; //ANSI:错误
    //ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。
    //例如:
    int *pint;
    pint++; //ANSI:正确

    pint++ 的结果是使其增大 sizeof(int)。

    但是 GNU 则不这么认定,它指定 void * 的算法操作与 char * 一致。因此下列语句在 GNU 编译器中皆正确:

    pvoid++; //GNU:正确
    pvoid += 1; //GNU:正确

    pvoid++ 的执行结果是其增大了 1。

    在实际的程序设计中,为迎合 ANSI 标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:

    void * pvoid;
    ((char *)pvoid)++; //ANSI:错误;GNU:正确
    (char *)pvoid += 1; //ANSI:错误;GNU:正确

    GNU 和 ANSI 还有一些区别,总体而言,GNU 较 ANSI 更"开放",提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合 ANSI 标准。 如果函数的参数可以是任意类型指针,那么应声明其参数为void *。

    注:void 指针可以任意类型的数据,可以在程序中给我们带来一些好处,函数中形为指针类型时,我们可以将其定义为 void 指针,这样函数就可以接受任意类型的指针。如:

    典型的如内存操作函数 memcpy 和 memset 的函数原型分别为:

    void * memcpy(void *dest, const void *src, size_t len);
    void * memset ( void * buffer, int c, size_t num );

    这样,任何类型的指针都可以传入 memcpy 和 memset 中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型(参见 C 语言实现泛型编程)。如果 memcpy 和 memset 的参数类型不是 void *,而是 char *,那才叫真的奇怪了!这样的 memcpy 和 memset 明显不是一个"纯粹的,脱离低级趣味的"函数!void 的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中"抽象基类"的概念,也很容易理解 void 数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个 void(让我们类比的称 void 为"抽象数据类型")变量。

    c语言泛型编程

    转载:https://www.runoob.com/w3cnote/c-general-function.html

    C语言实现泛型编程

    泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。在 C 语言中,可以通过一些手段实现这样的泛型编程。这里介绍一种方法——通过无类型指针 void*。

    看下面的一个实现交换两个元素内容的函数 swap,以整型 int 为例:

    void swap(int* i1,int* i2){  
             int temp;  
             temp = *i1;  
             *i1 = *i2;  
             *i2 = temp;  
    }

    当你想交换两个 char 类型时,你还得重写一个参数类型为 char 的函数,是不是能用无类型的指针来作为参数呢?看如下改动:

    void swap(void *vp1,void *vp2){  
           void temp = *vp1;  
           *vp1 = *vp2;  
           *vp2 = temp;  
    }

    这段代码是错误的,是通不过编译的。首先,变量是不能声明为 void 无类型的。而你不知道调用此函数传进的参数是什么类型的,无法确定一种类型的声明。同时,不能将 * 用在无类型指针上,因为系统没有此地址指向对象大小的信息。在编译阶段,编译器无法得知传入此函数参数的类型的。这里要想实现泛型的函数,需要在调用的地方传入相关要交换的对象的地址空间大小 size,同时利用在头文件 string.h 中定义的 memcpy() 函数来实现。改动如下:

    void swap(void *vp1,void *vp2,int size){  
          char buffer[size];//注意此处gcc编译器是允许这样声明的
          memcpy(buffer,vp1,size);  
          memcpy(vp1,vp2,size);  
          memcpy(vp2,buffer,size);  
    }

    在调用这个函数时,可以像如下这样调用(同样适用于其它类型的 x、y):

    int x = 27,y = 2;  
    swap(&x,&y,sizeof(int));

    下面看另一种功能的函数:

    int lsearch(int key,int array[],int size){
          for(int i = 0;i < size; ++i)
                      if(array[i] == key)
                               return i;
         return -1;
    }

    此函数在数组 array 中查找 key元素,找到后返回它的索引,找不到返回 -1。

    如上,也可以实现泛型的函数:

    void* lsearch(void* key, void *base, int n, int elemSize){
        for(int i = 0;i < n; ++i){
            void *elemAddr = (char *)base+i*elemSize;
            if(memcmp(key, elemAddr, elemSize) == 0)
                return elemAddr;
        }
        return NULL;
    }

    代码第三行:将数组的首地址强制转换为指向 char 类型的指针,是利用 char 类型大小为1字节的特性,使 elemAddr 指向此"泛型"数组的第 i-1 个元素的首地址。因为之前已经说过,此时你并不知道你传入的是什么类型的数据,系统无法确定此数组一个元素有多长,跳向下个元素需要多少字节,所以强制转换为指向 char 的指针,再加上参数传入的元素大小信息和累加数i的乘积,即偏移地址,即可得此数组第 i-1 个元素的首地址。这样使无论传入的参数是指向什么类型的指针,都可以得到指向正确元素的指针,实现泛型编程。

    函数 memcmp() 原型:int memcmp(void *dest,const void *src,int n),比较两段长度为n首地址分别为 dest、src 的地址空间中的内容。

    此函数在数组 base 中查找 key 元素,找到则返回它的地址信息,找不到则返回 NULL。

    如果使用函数指针,则可以实现其行为的泛型:

    void *lsearch(void *key,void *base,int n,int elemSize,int(*cmpfn)(void*,void*,int)){
        for(int i = 0;i < n; ++i){
            void *elemAddr = (char *)base+i*elemSize;
            if(cmpfn(key,elemAddr,elemSize) == 0)
                return elemAddr;
        }
        return NULL;
    }

    再定义一个要调用的函数:

    int intCmp(void* elem1,void* elem2){
            int* ip1 = elem1;
            int* ip2 = elem2;
            return *ip1-*ip2;
    }

    看如下调用:

    int array[] = {1,2,3,4,5,6};
    int size = 6;
    int number = 3;
    int *found = lsearch(&number,array,size,sizeof(int),intCmp);
    if(found == NULL)
             printf("NO\n");
    else
             printf("YES\n");

    C 语言也可以实现一定的泛型编程,但这样是不安全的,系统对其只有有限的检查。在编程时一定要多加细心。

    转载:https://blog.csdn.net/u011028345/article/details/64905175#:~:text=void%20%2A,data%20%C2%A0%E6%98%AF%E4%B8%80%E4%B8%AA%E6%97%A0%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%8C%87%E9%92%88%E5%8F%82%E6%95%B0%EF%BC%8C%E4%BB%BB%E6%84%8F%E7%B1%BB%E5%9E%8B%E6%8C%87%E9%92%88%E5%8F%AF%E4%BB%A5%E8%B5%8B%E5%80%BC%E7%BB%99data%EF%BC%8C%E5%BD%93data%E8%B5%8B%E5%80%BC%E7%BB%99%E5%85%B6%E4%BB%96%E7%B1%BB%E5%9E%8B%E6%97%B6%E9%9C%80%E8%A6%81%E5%85%88%E6%8C%87%E5%AE%9A%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E3%80%82

    void * data 数据类型参数解释

    void * data 是一个无类型的指针参数,任意类型指针可以赋值给data,当data赋值给其他类型时需要先指定数据类型。

    代码示例:

    int add(void *a,void *b)
    {
    int c;
    c=*((int*)a)+*((int*)b);
    return c;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    int a=1,b=2;
    int sum;
    sum=add(&a,&b);
    cout<<sum<<endl;
    getchar();
    return 0;
    }

  • 相关阅读:
    模型绑定功能
    接口返回的内容
    跨平台的ASP.NET Core简介
    NLog如何打印日志(.Net5)
    注意力创造价值;
    Restful接口的介绍
    电脑设置双屏显示(windows)
    Linq多集合连接
    调试时才执行的代码
    mvc4 路由匹配测试
  • 原文地址:https://www.cnblogs.com/zhiminyu/p/16349307.html
Copyright © 2020-2023  润新知