C语言泛型 _Generic

news2025/10/24 3:24:25

C11 标准中的 _Generic 关键字实现泛型编程

在 C11 标准中,_Generic 关键字为 C 语言引入了轻量级的泛型编程能力。尽管 C 语言不像 C++ 那样支持面向对象编程和模板,但它通过 _Generic 提供了一种在编译时根据表达式的类型选择不同代码路径的方式。这使得 C 语言能够在某种程度上实现类似于泛型编程的设计。

什么是泛型编程?

泛型编程是一种编程范式,它允许程序员在编写代码时使用一些将来才会指定的类型。这些类型在代码实例化时作为参数指明。例如,在 C++ 中,可以通过模板来支持泛型编程。

std::vector<T>, std::list<T>, std::set<T>

C 语言中的泛型编程

在 C 语言中,虽然没有真正意义上的泛型编程,但 C11 标准中的 _Generic 关键字提供了一种在编译时根据赋值表达式的类型在泛型关联表中选择一个表达式的方法。这样可以将一组功能相同但类型不同的函数抽象为一个统一的接口。

_Generic 的语法形式

_Generic 关键字的基本语法如下:

_Generic ( assignment-expression , generic-assoc-list )

其中:

  • assignment-expression:赋值表达式,可以认为是变量 var
  • generic-assoc-list:泛型关联表,其语法为:
    type-name : expression, type-name : expression, ..., default : expression
    
示例代码

让我们通过一个具体的例子来理解如何使用 _Generic 实现泛型编程。

实现 getTypeName 函数

假设我们想要实现一个 getTypeName 函数,该函数返回变量 var 的类型名称。可以这样写:

#define GET_TYPENAME(var) _Generic((var), \
    int: "int", \
    char: "char", \
    float: "float", \
    double: "double", \
    char*: "char *", \
    default: "other type")

int main(int argc, char const *argv[]) {
    int x;
    int* x1;
    char s[10];

    printf("type: x = %s\n", GET_TYPENAME(x));
    printf("type: x1 = %s\n", GET_TYPENAME(x1));
    printf("type: s = %s\n", GET_TYPENAME(s));

    return 0;
}

运行结果:

type: x = int
type: x1 = other type
type: s = char *
C语言实现泛型动态数组

首先实现一个整形的动态数组:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 动态数组结构体
typedef struct {
    int capacity;  // 数组容量
    int count;     // 当前元素数量
    int data[];    // 零长度数组
} DynamicArray;

// 初始化动态数组
DynamicArray* init_dynamic_array(int initial_capacity) {
    // 为结构体和元素分配足够的内存
    DynamicArray* array = (DynamicArray*)malloc(sizeof(DynamicArray) + initial_capacity * sizeof(int));
    if (array) {
        array->capacity = initial_capacity;
        array->count = 0;
    }
    return array;
}

// 增加数组容量
DynamicArray* ensure_capacity(DynamicArray* array, int min_capacity) {
    if (min_capacity > array->capacity) {
        int new_capacity = (array->capacity * 3) / 2 + 1; // 新容量至少增加50%
        if (new_capacity < min_capacity) {
            new_capacity = min_capacity;
        }
        // 重新分配内存以适应新的大小
        DynamicArray* new_array = (DynamicArray*)realloc(array, sizeof(DynamicArray) + new_capacity * sizeof(int));
        if (new_array) {
            new_array->capacity = new_capacity;
            return new_array;
        }
    }
    return array; // 如果不需要扩容,返回原数组
}

// 向动态数组中添加元素
void append(DynamicArray** array, int element) {
    // 先确保有足够的容量
    *array = ensure_capacity(*array, (*array)->count + 1);
    // 添加元素
    (*array)->data[(*array)->count] = element;
    (*array)->count++;
}

// 打印动态数组的内容
void print_array(const DynamicArray* array) {
    for (int i = 0; i < array->count; ++i) {
        printf("%d ", array->data[i]);
    }
    printf("\n");
}

// 销毁动态数组
void destroy_dynamic_array(DynamicArray* array) {
    free(array);
}

int main() {
    int initial_capacity = 5;
    printf("sizeof DynamicArray: %ld\n", sizeof(DynamicArray));
    DynamicArray* array = init_dynamic_array(initial_capacity); // 初始容量为5

    // 添加一些元素
    for (int i = 0; i < 10; ++i) {
        append(&array, i);
    }

    // 打印动态数组
    print_array(array);

    // 销毁动态数组
    destroy_dynamic_array(array);

    return 0;
}

下面我们再实现一个泛型的动态数组

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 泛型动态数组结构体
typedef struct {
    int capacity;        // 数组容量
    int count;           // 当前元素数量
    size_t elem_size;    // 元素大小
    int (*data)[0];      // 零长度数组
} GenericDynamicArray;

// 初始化泛型动态数组
GenericDynamicArray* init_generic_dynamic_array(int initial_capacity, size_t elem_size) {
    // 为结构体和元素分配足够的内存
    GenericDynamicArray* array = (GenericDynamicArray*)malloc(sizeof(GenericDynamicArray) + initial_capacity * elem_size);
    if (array) {
        array->capacity = initial_capacity;
        array->count = 0;
        array->elem_size = elem_size;
        array->data = (int(*)[0])(((char*)array) + sizeof(GenericDynamicArray)); // 计算数据起始地址
    }
    return array;
}

// 增加泛型动态数组容量
void ensure_capacity(GenericDynamicArray *array, size_t min_capacity) {
    if (min_capacity > array->capacity) {
        size_t new_capacity = (array->capacity * 3) / 2 + 1;
        if (new_capacity < min_capacity) {
            new_capacity = min_capacity;
        }
        array = realloc(array, sizeof(GenericDynamicArray) + new_capacity * array->elem_size);
        array->capacity = new_capacity;
    }
}

// 向泛型动态数组中添加元素
#define append(array, element) _Generic((element), \
    int: append_int, \
    float: append_float, \
    char: append_char \
    )(array, element)


// 特化版本的 append 函数
void append_int(GenericDynamicArray *array, int element) {
    ensure_capacity(array, array->count + 1);
    ((int *)array->data)[array->count] = element;
    array->count++;
}

void append_float(GenericDynamicArray *array, float element) {
    ensure_capacity(array, array->count + 1);
    ((float *)array->data)[array->count] = element;
    array->count++;
}

void append_char(GenericDynamicArray *array, char element) {
    ensure_capacity(array, array->count + 1);
    ((char *)array->data)[array->count] = element;
    array->count++;
}

// 打印泛型动态数组的内容
void print_array(const GenericDynamicArray* array, void (*print)(const void *)) {
    for (int i = 0; i < array->count; ++i) {
        print((char*)array->data + i * array->elem_size);
    }
    printf("\n");
}

// 泛型打印函数
void generic_print_int(const void *data) {
    printf("%d ", *(int *)data);
}

void generic_print_long(const void *data) {
    printf("%ld ", *(long *)data);
}

void generic_print_double(const void *data) {
    printf("%f ", *(double *)data);
}

void generic_print_char(const void *data) {
    printf("%c ", *(char *)data);
}

void generic_print_void(const void *data) {
    printf("%p ", data);
}

int main() {
    int initial_capacity = 5;
    printf("sizeof GenericDynamicArray: %ld\n", sizeof(GenericDynamicArray));
    
    GenericDynamicArray* array_int = init_generic_dynamic_array(initial_capacity, sizeof(int)); // 初始容量为5

    // 添加一些整数元素
    int int_data[] = {1, 2, 3, 4, 5, 6, 7 , 8, 9};
    for (int i = 0; i < sizeof(int_data) / sizeof(int); ++i) {
        append(array_int, int_data[i]);
    }

    // 打印动态数组
    print_array(array_int, generic_print_int);

    // 销毁动态数组
    free(array_int);

    GenericDynamicArray* array_char = init_generic_dynamic_array(initial_capacity, sizeof(char)); // 初始容量为5

    char char_data[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'};
    for (int i = 0; i < sizeof(char_data) / sizeof(char); ++i) {
        append(array_char, char_data[i]);
    }

    print_array(array_char, generic_print_char);

    free(array_char);

    return 0;
}

这个示例虽然看着比较笨,但是我们仍然实现了append的泛型。

注意事项

  1. default 不是必须的default 子句不是必须的,但在实际应用中建议包含,以处理未预期的类型。
  2. 表达式仅仅是宏替换generic-assoc-list 中的 expression 仅仅是普通的宏替换,因此可以自由地放置参数位置。

总结

通过使用 _Generic 关键字,C 语言可以在一定程度上实现泛型编程,使程序更加灵活和通用。然而,需要注意的是,_Generic 的使用仅限于编译时类型选择,而且表达式只是简单的宏替换。此外,对于复杂的类型和多种参数组合,还需要进一步考虑如何正确处理不同类型之间的转换。

欢迎关注:GarenJian

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2136056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32+ESP01连接到机智云

机智云,全球领先的智能硬件软件自助开发及物联网(iot)云服务平台。机智云平台为开发者提供了自助式智能硬件开发工具与开放的云端服务。通过傻瓜化的自助工具、完善的SDK与API服务能力最大限度降低了物联网硬件开发的技术门槛&#xff0c;降低开发者的研发成本&#xff0c;提升…

添加选择登录ssh终端

吼吼,这次成了一个小的瑞士军刀了 … …

2.Jmeter安装配置,核心目录详情,组件和作用域

一、Jmeter安装配置以及核心目录详情 Jmeter基于java语言来开发&#xff0c;java需要jdk环境。 1.安装jdk并且配置jdk的环境变量。 2.jmeter只需要解压就可以使用了。 3.在D:\apache-jmeter-5.5\bin目录下双击jmeter.bat文件就可以启动使用了 backups&#xff1a;自动备份的目录…

OpenHarmony(鸿蒙南向开发)——轻量系统STM32F407芯片移植案例

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ OpenHarmony&#xff08;鸿蒙南向开发&#xff09;——轻量和小型系统三方库移植指南…

JAVA毕业设计170—基于Java+Springboot+vue3+小程序的房屋租赁小程序系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootvue3小程序的房屋租赁小程序系统(源代码数据库)170 一、系统介绍 本项目前后端分离(可以改为ssm版本)&#xff0c;分为用户、房东、管理员三种角色 1、用户&am…

华为云分布式缓存服务DCS 8月新特性发布

分布式缓存服务&#xff08;Distributed Cache Service&#xff0c;简称DCS&#xff09;是华为云提供的一款兼容Redis的高速内存数据处理引擎&#xff0c;为您提供即开即用、安全可靠、弹性扩容、便捷管理的在线分布式缓存能力&#xff0c;满足用户高并发及数据快速访问的业务诉…

[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking)

前言 最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能&#xff0c;但是遇到了如下问题&#xff1a; 在Unity里面没法串流调试眼动追踪功能&#xff0c;根本获取不到Device&#xff0c;只能将整个场景build成APK&#xff0c;安装到头盔里&#xff0c;才能在…

明星代言推广的6种优势,打造品牌巅峰!

在当今的商业社会中&#xff0c;品牌推广成为了企业发展不可或缺的一环。对于许多企业来说&#xff0c;明星代言已经成为了一种常见的推广策略。本文将介绍明星代言推广的六种优势&#xff0c;帮助企业了解并充分利用这一策略&#xff0c;从而打造品牌的巅峰&#xff01; 1. 塑…

java中SPI(服务提供者的接口)

java中SPI&#xff08;服务提供者的接口&#xff09; 一&#xff1a;什么是SPI二&#xff1a;java SPI示例1.SPI服务提供方2.SPI服务应用方开发者 三&#xff1a;JavaSPI 机制的核心-ServiceLoader 一&#xff1a;什么是SPI SPI&#xff1a;“服务提供者的接口”&#xff0c;是…

USART—串口数据包

1.HEX数据包定义 数据包的作用是把一个个单独的数据给打包起来&#xff0c;方便我们进行多字节的数据通信&#xff0c;在实际应用中&#xff0c;我们可能需要把多个字节打包为一个整体进行发送&#xff0c;比如说&#xff0c;我们有个陀螺仪传感器&#xff0c;需要用串口发送数…

git 你要如何打开这个文件

终端输入git命令都会弹出这个框 解决方案&#xff1a; 参考文章&#xff1a;在vscode终端上运行 npm 会询问 “你要如何打开这个文件“_安装 npm脚手架提示你要如何打开-CSDN博客 get-command git后删除对应文件 请注意不要删错了&#xff0c;正常get-command git后对应的是…

微型导轨加工环境需避免的隐患!

微型导轨是一种小巧精密的线性定位解决方案&#xff0c;其高速度、低噪音的特点使得它在现代制造业中扮演着越来越重要的角色。而微型导轨对于加工环境的要求主要体现在以下几个方面&#xff1a; 1、温度控制&#xff1a;加工环境需要保持在适宜的温度范围内&#xff0c;过高或…

【西电电装实习】5. 无人机模块及作用、上位机的操作

文章目录 前言一、硬件结构电源、电源电压测试电路晶振外围陀螺仪信号放大电路及天线空心杯&#xff08;电极&#xff09;驱动电路 软件设置整机装配PID 参数设置公式 参考文献 前言 西电电装实习&#xff0c;无人机原理图、上位机的调节方法 一、硬件结构 电源、电源电压测…

RabbitMQ 基础入门

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 前言 1. 重要概念 1.1 Publisher 1.2 Message 1.3 Exchange 1.4 BindingKey 1.5 Routingkey 1.6 Queue 1.7 Consumer 1.8 Connection 1.9 Channel 1.10 Virtual Host 1.11Broker 2. RabbitMQ…

个人学习笔记7-5:动手学深度学习pytorch版-李沐

#人工智能# #深度学习# #语义分割# #计算机视觉# #神经网络# 计算机视觉 13.10 转置卷积 例如&#xff0c;卷积层和汇聚层&#xff0c;通常会减少下采样输入图像的空间维度&#xff08;高和宽&#xff09;。然而如果输入和输出图像的空间维度相同&#xff0c;在以像素级分类…

手机玩机常识____展讯芯片刷机平台ResearchDownload的一些基本常识与问题解决

展讯ResearchDownload工具 展讯芯片的刷机工具--ResearchDownload下载工具"是一款专为用户设计的高效、便捷的下载管理软件&#xff0c;它能够帮助用户快速、稳定地从互联网上获取各种文件。这款工具以其强大的功能和良好的用户体验&#xff0c;在众多展讯芯片下载工具中脱…

计算机网络30——Linux-gdb调试命令makefile

1、开始调试 编译时带-g为调试&#xff0c;带调试信息编译后的可执行文件更大 2、进入调试 使用gdb 可执行文件名——进入调试 失败版&#xff1a; 成功版&#xff1a; 3、l命令 l什么都不加——列出10行代码 l 行号——行号的行在中间&#xff0c;向上向下展示10行 4、st…

ppt文件怎么压缩变小一些?8种压缩PPT文件的方法推荐

ppt文件怎么压缩变小一些&#xff1f;在现代工作环境中&#xff0c;PPT文件常常是我们展示信息和分享想法的主要工具。然而&#xff0c;当这些文件变得庞大时&#xff0c;它们不仅会占用大量的存储空间&#xff0c;还可能导致处理速度变慢&#xff0c;影响整体工作效率。这种情…

GIS在线监测SF6密度微水定量检漏仪传感器专用波纹管免焊接格兰头

SF6密度微水在线监测装置配套M12母头5孔格兰头穿波纹管连接器&#xff0c;该传感器能够监测SF6气体的露点&#xff0c;从而实现对SF6气体在线监测。 原理 SF6露点在线监测传感器是一种可以监测SF6气体露点的传感器它的工作原理是通过吸收SF6气体中的水分子来测量SF6气体的露点。…

Jetpack PDF库:解锁Android应用中的PDF功能

Jetpack PDF库&#xff1a;解锁Android应用中的PDF功能 在 Android 开发中&#xff0c;加载 PDF 文件是一个比较常见的需求&#xff0c;但就是这么一个比较常见的功能&#xff0c;原生并没有提供一个比较好的方案&#xff0c;而隔壁 IOS 中实现却是很方便的。。。 下面是当前…