泛型指针#

C 的泛型编程极大程度依赖 void* 泛型指针,不关联任何数据类型。

  • 任何数据类型的地址或指针都可以赋值给 void*void* 也可以赋值给任何数据类型的指针。

    int arr[] = {2, 4, 6, 8, 10};
    void* arr_p1 = arr;      //  int* ->   void*  
    double* arr_p2 = arr_p1; // void* -> double*
    
  • void* 不可以直接解引用,必须搭配类型转换。

    // Buggy #1: void pointers cannot be dereferenced
    int a = 10;
    void* a_ptr = &a;
    // printf("%d", *a_ptr);        // error
    printf("%d\n", * (int*) a_ptr);
    
  • void* 不可以直接进行指针算术运算,必须转换为 char* 等具体类型才可以进行算术运算。

    // Buggy #2: void pointers doesn’t allow pointer arithmetic
    double b[2] = {1.0, 2.0};
    void* b_ptr = &b;
    // b_ptr = b_ptr + sizeof(double);     // error
    b_ptr = (char*)b_ptr + sizeof(double);
    // or
    b_ptr = (double*)b_ptr + 1;
    

由于内存分配函数 malloc/calloc 等返回的是 void* 类型,所以可以用于分配任何数据类型的内存。除此之外,void* 还可以用于实现泛型函数。

泛型选择表达式#

C11 标准引入了一个泛型选择(generic selection)特性,通过宏语句提供泛型支持。其语法类似 switch-case 语句,但区别是宏替换是发生在编译期的,类似 C++ 的模板技术。

#include <stdio.h>
#include <math.h>
 
// 实现泛型 cbrt 函数
#define cbrt(X) _Generic((X), \
              long double: cbrtl, \
                  default: cbrt,  \
                    float: cbrtf  \
              )(X)
 
int main(void) {
    double x = 8.0;
    const float y = 3.375;
    printf("cbrt(8.0) = %f\n", cbrt(x));    // 类型为 double,选择默认的 cbrt 函数
    printf("cbrtf(3.375) = %f\n", cbrt(y)); // 类型为 float,自动选择 cbrtf 函数
}

感兴趣的同学可以参考在文档和示例程序:Generic selection (since C11)。关于该技术,本课程不作更多叙述。