使用纯C语言定义通用型数据结构的方法和示例

news2024/11/25 13:25:15

文章目录

  • 前言
  • 以实现优先队列来描述实现思想
  • 基本类型的包装类型
  • 比较函数
  • 演示
  • 总结

前言

最近一段时间在复习数据结构和算法,用的C语言,不得不说,不学个高级语言再回头看C语言根本不知道C语言的强大和完美,不过相比之下也有许多不便利的地方,尤其是以下两个方面:

  • 没有异常处理机制
  • 没有泛型

其中第一方面之前就解决了,详情请看在C语言中实现类似面向对象语言的异常处理机制,今天下午有空来实现一下泛型。不得不说,通过异常处理机制和泛型的实现,既让我C语言使用的得心应手,又让我对高级语言的设计有了亲身般体验。

以实现优先队列来描述实现思想

首先C语言本身不支持泛型,这意味着实现泛型有以下两个困难(解决这两个困难也就意味着成功):

  • ①:类型信息在编译前就已经确定了
  • ②:类型信息不能像参数一样传递

有了目标就轻松多了,于是我立刻就想到了函数的可变参数<stdarg.h>,请看下面的DEMO:

PackagingTypeList intBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, int);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

在使用va_arg取可变参数时我们确实直接将int类型作为参数传递了,这就意味着困难②克服了,那么困难①呢?于是我继续研究,我发现函数的参数存储在一个GCC内置的数据结构中:

typedef struct {
       void *__stack;					/* __stack 记录下一个匿名栈参数的存储位置, 随着va_arg的调用可能变化 */
       void *__gr_top;					/* __gr_top 记录最后一个匿名通用寄存器参数的尾地址, 其不随va_arg调用变化 */
       void *__vr_top;					/* __vr_top 记录最后一个匿名浮点寄存器参数的尾地址, 其不随va_arg调用变化 */
       int   __gr_offs;				    /* __gr_offs 记录下一个匿名通用寄存器参数到__gr_top的偏移(负数),随着va_arg的调用可能变化 */
       int   __vr_offs;					/* __vr_offs 记录下一个匿名浮点寄存器参数到__vr_top的偏移(负数),随着va_arg的调用可能变化 */
} __builtin_va_list;

这就意味着要想克服困难①就必须得到编译器的支持,显然这是不可能的,于是我果断放弃了,但困难②的克服给我了灵感,va_arg是一个宏定义,强大的预处理器赋予了C语言元编程的能力,这就是我想到的第一种方法:

  • 克服困难①:使用宏定义在编译时定义可以存储指定类型的优先队列,
  • 克服困难②:使用带参数的宏传递类型信息

于是第一种方案诞生了:

#define PriorityQueueNode(TYPE)                                                     \
{                                                                                   \
    typedef struct PriorityQueueNode_##TYPE{                                        \
        TYPE data;                                                                  \
        struct PriorityQueueNode *next;                                             \
        struct PriorityQueueNode *prior;                                            \
    }PriorityQueueNode_##TYPE;                                                      \
}while(false)

#define priorityQueueEnQueue(TYPE)                                                  \
{                                                                                   \
    void priorityQueueEnQueue_##TYPE(struct PriorityQueue_##TYPE queue,TYPE data){  \
        ...                                                                       \
    }                                                                               \
}while(false)

#define PriorityQueue(TYPE, NAME)                                                   \
{                                                                                   \
    PriorityQueueNode(TYPE);                                                        \
    priorityQueueEnQueue(TYPE)                                                      \
    PriorityQueueNode_##TYPE head={.next=NULL,.prior=NULL};                         \
    struct PriorityQueue_##TYPE{                                                    \
        PriorityQueueNode *front;                                                   \
        PriorityQueueNode *rear;                                                    \
        void (* priorityQueueEnQueue)(struct PriorityQueue_##TYPE,TYPE);            \
    } NAME={                                                                        \
       .front=&head,                                                                \
       .rear=&head                                                                  \
       .priorityQueueEnQueue=priorityQueueEnQueue_##TYPE                            \
    };                                                                              \
}while(false)

不过还没等写完我就放弃了,因为这太不优雅了,这其实和单独为每种类型定义一个优先队列没什么区别,于是我又想到了void*指针,这就是我想到的第二个方法,也是最终实现的方法:

  • 克服困难①:使用void*指针存储任意数据类型的指针,实际存储数据的空间由调用者分配
  • 克服困难②:在数据结构内部需要类型信息的地方通过传入的函数完成,这个函数也由调用者提供
//PriorityQueue.h

#ifndef INC_2023_PRIORITYQUEUE_H
#define INC_2023_PRIORITYQUEUE_H

#include "../../../util/Util.h"

typedef struct PriorityQueueNode PriorityQueueNode;
typedef struct PriorityQueue *PriorityQueue;

/**
 * 构造带头结点的优先队列
 * @param compare
 * @return
 */
PriorityQueue priorityQueueConstructor(int (*compare)(void *, void *)) throws NULL_POINTER_EXCEPTION;

/**
 * 销毁优先队列
 * @param queue
 */
void priorityQueueFinalize(PriorityQueue queue) throws NULL_POINTER_EXCEPTION;

/**
 * 优先队列是否为空
 * @param queue
 * @return
 */
bool priorityQueueIsEmpty(PriorityQueue queue) throws NULL_POINTER_EXCEPTION;

/**
 * 入队
 * @param queue
 * @param element
 */
void priorityQueueEnQueue(PriorityQueue queue, void *element) throws NULL_POINTER_EXCEPTION;

/**
 * 出队
 * @param queue
 * @return
 */
void *priorityQueueDeQueue(PriorityQueue queue) throws NULL_POINTER_EXCEPTION;


#endif //INC_2023_PRIORITYQUEUE_H
//PriorityQueue.c

#include "PriorityQueue.h"

struct PriorityQueueNode {
    void *data;
    PriorityQueueNode *next;
    PriorityQueueNode *prior;
};

struct PriorityQueue {
    PriorityQueueNode *front;
    PriorityQueueNode *rear;

    int (*compare)(void *, void *);
};

/**
 * 构造带头结点的优先队列
 * @param compare
 * @return
 */
PriorityQueue priorityQueueConstructor(int (*compare)(void *, void *)) throws NULL_POINTER_EXCEPTION {
    if (compare == NULL) {
        throw Error(NULL_POINTER_EXCEPTION, "比较函数不能为空");
    }
    PriorityQueue queue = malloc(sizeof(struct PriorityQueue));
    //头结点
    queue->front = queue->rear = malloc(sizeof(PriorityQueueNode));
    queue->front->next = NULL;
    queue->front->prior = NULL;
    queue->compare = compare;
    return queue;
}

/**
 * 销毁优先队列
 * @param queue
 */
void priorityQueueFinalize(PriorityQueue queue) throws NULL_POINTER_EXCEPTION {
    if (queue == NULL) {
        throw Error(NULL_POINTER_EXCEPTION, "优先队列不能为空");
    }
    for (; !priorityQueueIsEmpty(queue);) {
        priorityQueueDeQueue(queue);
    }
    free(queue->front);
    free(queue);
}

/**
 * 优先队列是否为空
 * @param queue
 * @return
 */
bool priorityQueueIsEmpty(PriorityQueue queue) throws NULL_POINTER_EXCEPTION {
    if (queue == NULL) {
        throw Error(NULL_POINTER_EXCEPTION, "优先队列不能为空");
    }
    if (queue->front == queue->rear) {
        return true;
    } else {
        return false;
    }
}

/**
 * 入队
 * @param queue
 * @param element
 */
void priorityQueueEnQueue(PriorityQueue queue, void *element) throws NULL_POINTER_EXCEPTION {
    if (queue == NULL) {
        throw Error(NULL_POINTER_EXCEPTION, "优先队列不能为空");
    }
    PriorityQueueNode *node = malloc(sizeof(PriorityQueueNode));
    node->data = element;
    //如果新加入元素优先级比队尾元素优先级小则直接插入队尾,否则就遍历优先队列找到合适的插入位置
    if (priorityQueueIsEmpty(queue) || queue->compare(queue->rear->data, node->data) > 0) {
        node->next = NULL;
        node->prior = queue->rear;
        queue->rear->next = node;
        queue->rear = node;
    } else {
        for (PriorityQueueNode *temp = queue->front->next; temp != NULL; temp = temp->next) {
            if (queue->compare(temp->data, node->data) <= 0) {
                node->next = temp;
                node->prior = temp->prior;
                temp->prior->next = node;
                temp->prior = node;
                break;
            }
        }
    }
}

/**
 * 出队
 * @param queue
 * @return
 */
void *priorityQueueDeQueue(PriorityQueue queue) throws NULL_POINTER_EXCEPTION {
    if (queue == NULL) {
        throw Error(NULL_POINTER_EXCEPTION, "优先队列不能为空");
    }
    if (!priorityQueueIsEmpty(queue)) {
        PriorityQueueNode *node = queue->front->next;
        void *data = node->data;
        if (queue->rear == node) {
            queue->rear = queue->front;
            queue->front->next = NULL;
        } else {
            queue->front->next = node->next;
            node->next->prior = queue->front;
        }
        free(node);
        return data;
    } else {
        return NULL;
    }
}

基本类型的包装类型

为了方便基本类型指针的获取,我定义了基本类型的包装类型:

//PackagingType.h

#ifndef DSA_PACKAGINGTYPE_H
#define DSA_PACKAGINGTYPE_H

#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>

typedef union PackagingType PackagingType, **PackagingTypeList;

int getIntValue(void *element);

float getFloatValue(void *element);

double getDoubleValue(void *element);

char getCharValue(void *element);

bool getBoolValue(void *element);

PackagingType *intValueOf(int value);

PackagingTypeList intBatchValueOf(int size, ...);

PackagingType *floatValueOf(float value);

PackagingTypeList floatBatchValueOf(int size, ...);

PackagingType *doubleValueOf(double value);

PackagingTypeList doubleBatchValueOf(int size, ...);

PackagingType *charValueOf(char value);

PackagingTypeList charBatchValueOf(int size, ...);

PackagingType *boolValueOf(bool value);

PackagingTypeList boolBatchValueOf(int size, ...);

#endif //DSA_PACKAGINGTYPE_H

//PackagingType.c

union PackagingType {
    int intValue;
    float floatValue;
    double doubleValue;
    char charValue;
    bool boolValue;
};

int getIntValue(void *element) {
    return ((PackagingType *) element)->intValue;
}

float getFloatValue(void *element) {
    return ((PackagingType *) element)->floatValue;
}

double getDoubleValue(void *element) {
    return ((PackagingType *) element)->doubleValue;
}

char getCharValue(void *element) {
    return ((PackagingType *) element)->charValue;
}

bool getBoolValue(void *element) {
    return ((PackagingType *) element)->boolValue;
}

PackagingType *intValueOf(int value) {
    union PackagingType *pack = malloc(sizeof(PackagingType));
    pack->intValue = value;
    return pack;
}

PackagingTypeList intBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, int);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

PackagingType *floatValueOf(float value) {
    union PackagingType *pack = malloc(sizeof(PackagingType));
    pack->floatValue = value;
    return pack;
}

PackagingTypeList floatBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, double);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

PackagingType *doubleValueOf(double value) {
    union PackagingType *pack = malloc(sizeof(PackagingType));
    pack->doubleValue = value;
    return pack;
}

PackagingTypeList doubleBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, double);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

PackagingType *charValueOf(char value) {
    union PackagingType *pack = malloc(sizeof(PackagingType));
    pack->charValue = value;
    return pack;
}

PackagingTypeList charBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, int);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

PackagingType *boolValueOf(bool value) {
    union PackagingType *pack = malloc(sizeof(PackagingType));
    pack->boolValue = value;
    return pack;
}

PackagingTypeList boolBatchValueOf(int size, ...) {
    PackagingTypeList list = calloc(size, sizeof(PackagingType *));
    va_list argList;
    va_start(argList, size);
    for (int i = 0; i < size; ++i) {
        union PackagingType *pack = malloc(sizeof(PackagingType));
        pack->intValue = va_arg(argList, int);
        *(list + i) = pack;
    }
    va_end(argList);
    return list;
}

比较函数

通过创建多个数据结构发现,在数据结构内部用到的往往是比较函数,因此,我把常用的比较函数都定义了一下:

//Comparable.h

#ifndef DSA_COMPARABLE_H
#define DSA_COMPARABLE_H

#include "../packaging-type/PackagingType.h"

extern int (*intCompare)(void *, void *);

extern int (*intPackCompare)(void *, void *);

extern int (*floatCompare)(void *, void *);

extern int (*floatPackCompare)(void *, void *);

extern int (*doubleCompare)(void *, void *);

extern int (*doublePackCompare)(void *, void *);

extern int (*charCompare)(void *, void *);

extern int (*charPackCompare)(void *, void *);

#endif //DSA_COMPARABLE_H
//Comparable.c

#include "Comparable.h"

int intComp(void *a, void *b) {
    return *((int *) a) - *((int *) b) > 0;
}

int intPackComp(void *a, void *b) {
    return getIntValue(a) - getIntValue(b) > 0;
}

int floatComp(void *a, void *b) {
    return *((float *) a) - *((float *) b) > 0;
}

int floatPackComp(void *a, void *b) {
    return getFloatValue(a) - getFloatValue(b) > 0;
}

int doubleComp(void *a, void *b) {
    return *((double *) a) - *((double *) b) > 0;
}

int doublePackComp(void *a, void *b) {
    return getDoubleValue(a) - getDoubleValue(b) > 0;
}

int charComp(void *a, void *b) {
    return *((char *) a) - *((char *) b) > 0;
}

int charPackComp(void *a, void *b) {
    return getCharValue(a) - getCharValue(b) > 0;
}

int (*intCompare)(void *, void *) =intComp;

int (*intPackCompare)(void *, void *) =intPackComp;

int (*floatCompare)(void *, void *) =floatComp;

int (*floatPackCompare)(void *, void *) =floatPackComp;

int (*doubleCompare)(void *, void *) =doubleComp;

int (*doublePackCompare)(void *, void *) =doublePackComp;

int (*charCompare)(void *, void *) =charComp;

int (*charPackCompare)(void *, void *) =charPackComp;

演示

首先看一个基本类型的例子:

#include "util/Util.h"
#include "linear-structure/queue/priority-queue/PriorityQueue.h"

int main() {
    PackagingTypeList list = intBatchValueOf(4, 1, 2, 3, 4);
    PriorityQueue queue = priorityQueueConstructor(intPackCompare);
    for (int i = 0; i < 4; ++i) {
        priorityQueueEnQueue(queue, *(list + i));
    }
    while (!priorityQueueIsEmpty(queue)) {
        printf("%d", getIntValue(priorityQueueDeQueue(queue)));
    }
    return 0;
}

在这里插入图片描述

再看一个结构类型的例子:

#include "util/Util.h"
#include "linear-structure/queue/priority-queue/PriorityQueue.h"

struct Student {
    int age;
    char *name;
};

int studentCompare(void *a, void *b) {
    return ((struct Student *) a)->age - ((struct Student *) b)->age > 0;
}

int main() {
    struct Student a = {.age=18, .name="张三"}, b = {.age=28, .name="李四"};
    PriorityQueue queue = priorityQueueConstructor(studentCompare);
    priorityQueueEnQueue(queue, &a);
    priorityQueueEnQueue(queue, &b);
    while (!priorityQueueIsEmpty(queue)) {
        printf("%s,", ((struct Student *) priorityQueueDeQueue(queue))->name);
    }
    return 0;
}

在这里插入图片描述

最后看一个抛异常的例子:

#include "util/Util.h"
#include "linear-structure/queue/priority-queue/PriorityQueue.h"

struct Student {
    int age;
    char *name;
};

int studentCompare(void *a, void *b) {
    return ((struct Student *) a)->age - ((struct Student *) b)->age > 0;
}

int main() {
    struct Student a = {.age=18, .name="张三"}, b = {.age=28, .name="李四"};
    PriorityQueue queue = priorityQueueConstructor(studentCompare);
    priorityQueueEnQueue(queue, &a);
    priorityQueueEnQueue(queue, &b);
    try {
        while (!priorityQueueIsEmpty(queue)) {
            printf("%s,", ((struct Student *) priorityQueueDeQueue(NULL))->name);//change to NULL
        }
    } catch(NULL_POINTER_EXCEPTION) {
        stdErr();
    }

    return 0;
}

在这里插入图片描述

总结

  • 第一种方法类似于C++的方式
  • 第二种方法类似于Java的方式

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

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

相关文章

Linux内核分析与应用

Linux 内核分析与应用[1] 蜻蜓点水,可作抛砖引玉 1.概述 用到的几个命令: insmod dmesg[2] lsmod[3] 章节测试: 部分可参考[4] <1>. Linux得以流行&#xff0c;是因为遵循了GPL协议&#xff0c;并不是因为遵循POSIX标准 (错) linux操作系统概述[5] linux概述[6] <2>…

建站系列(三)--- 网络协议

目录 相关系列文章前言一、定义二、术语简介三、协议的组成要素四、网络层次划分五、常见网络协议划分六、常用协议介绍&#xff08;一&#xff09;TCP/IP&#xff08;二&#xff09;HTTP协议&#xff08;超文本传输协议&#xff09;&#xff08;三&#xff09;SSH协议 相关系列…

robotframework 获取当前时间

1、获取年月日时分秒格式的数据 命令&#xff1a;${time} Get Current Date result_format%Y%m%d%H%M%S 日期时间显示结果显示形式&#xff1a;20230908102553 2、获取时间戳形式的数据 命令&#xff1a; ${time} Get Current Date result_formattimestamp …

私有gitlab的搭建和配置教程

文章目录 1. 说明2. 安装操作2.1 依赖项2.2 gitlab-ce2.3 简要配置2.4 网页操作2.5 中文配置2.6 其他操作 3. 配置https3.1 配置证书3.2 url配置3.3 网页访问3.4 认证错误 4. ssh操作4.1 生成文件4.2 web配置4.3 额外操作 1. 说明 此教程基于Ubuntu22.04进行阐述&#xff0c;只…

# Spring MVC与RESTful API:如何设计高效的Web接口

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

【MySQL】详解聚合查询、多表查询

MySQL 增删查改&#xff08;进阶&#xff09; 文章目录 MySQL 增删查改&#xff08;进阶&#xff09;01 表的设计表的三大范式 02 查询操作进阶新增聚合查询countsumavgmaxmin 分组查询 GROUP BYHAVING 联合查询/多表查询关键思路引入内连接外连接左外连接&#xff1a;left joi…

WAMP服务器对python进行配置

WAMP服务器中安装python WAMP服务器中安装python 步骤 D:\wamp\bin\apache\Apache2.4.4\conf\httpd.conf // 打开找到Wamp安装目录下&#xff0c;apache的配置文件 将该配置文件备份一份 LoadModule cgi_module modules/mod_cgi.so // 找到配置文件中该行&#xff0c;把行前面…

探索OLED透明屏触摸技术:创新引领智能生活的未来

OLED透明屏触摸技术作为一项创新技术&#xff0c;正在引领智能生活的未来。’ 通过将透明屏和触摸技术相结合&#xff0c;OLED透明屏触摸设备不仅具备了OLED显示屏的优势&#xff0c;还具备了触摸操作的便利性&#xff0c; 在这里&#xff0c;尼伽将介绍OLED透明屏触摸技术的…

Linux教程||Linux 系统启动过程

Linux 系统启动过程 linux启动时我们会看到许多启动信息。 Linux系统的启动过程并不是大家想象中的那么复杂&#xff0c;其过程可以分为5个阶段&#xff1a; 内核的引导。运行init。系统初始化。建立终端 。用户登录系统。 内核引导 当计算机打开电源后&#xff0c;首先是B…

Apache Hive之数据查询

文章目录 版权声明数据查询环境准备基本查询准备数据select基础查询分组、聚合JOINRLIKE正则匹配UNION联合Sampling采用Virtual Columns虚拟列 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程序员或相关权利…

Linux指令二【进程,权限,文件】

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程&#xff0c;是操作系统进行 资源分配和调度的一个独立单位&#xff0c;是应用程序运行的载体。 一、进程基本指令 1.ps&#xff1a;当前的用户进程 ps 只显示隶属于自己的进程状态ps -aux 显示所有进程…

JavaScript基础入门之输出

目录 目录 前言 系列文章列表 JavaScript初学 思维导图 1&#xff0c;如何在Edge浏览器中执行 JavaScript 1.1,首先按F12 1.2,使用控制台窗口调试js代码 1.2.1,如何使用 1.2.2,进行js测试使用 1.3,Edge小脚本 1.3.1,如何创建脚本 1.3.2,运行脚本 2&#xff0c;Ja…

CRC原理介绍及STM32 CRC外设的使用

1. CRC简介 循环冗余校验&#xff08;英语&#xff1a;Cyclic redundancy check&#xff0c;简称CRC&#xff09;&#xff0c;由 W. Wesley Peterson 于 1961 年首次提出的一种纠错码理论。 CRC是一种数据纠错方法&#xff0c;主要应用于数据通信或者数据存储的场合&#xff…

计算机专业毕业设计项目推荐02-个人医疗系统(Java+原生Js+Mysql)

个人医疗系统&#xff08;Java原生JsMysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程以…

Javase | String字符串-1

目录&#xff1a; 1.字符串 (String)2.new String对象 和 不new String对象时内存分析&#xff1a;2.1 不new String对象时内存分析2.2 new String对象时内存分析 3.以下程序一共创建了几个对象&#xff1f;4.String类中常用“构造方法”String( )String(byte [ ] b)String( by…

字符串逆序(不使用库函数并用递归实现)

文章目录 前言一、题目要求二、解题步骤1.大概框架2.如何反向排列&#xff1f;3.模拟实现strlen4.实现反向排列5.递归实现反向排列 总结 前言 嗨&#xff0c;亲爱的读者们&#xff01;我是艾老虎尤&#xff0c;。今天&#xff0c;我们将带着好奇心&#xff0c;探索一个题目&am…

显示器鼠标滚动时或者拖拽文字变为绿色

新电脑&#xff0c;新显示器&#xff0c;看文章时滚动鼠标滑轮&#xff0c;文字颜色就变为绿色。 拖住文本文档或者浏览器等有文字的窗口&#xff0c;文字也会变为绿色。 静止时一点儿问题没有。 以下视频展示滚动和拖拽的操作&#xff0c;视频看不出变色&#xff0c;只参考…

TuyaOS开发学习笔记(2)——NB-IoT开发SDK架构、运行流程

一、SDK架构 1.1 架构框图 基于 TuyaOS 系统&#xff0c;可以裁剪得到的适用于 NB-IoT 协议产品接入的 SDK。SDK 将设备配网、上下行数据通信、产测授权、固件 OTA 升级等接口进行封装&#xff0c;并提供相关函数。 1.2 目录结构 1.2.1 TuyaOS目录说明 adapter&#xff1a;T…

transformer 总结(超详细-初版)

相关知识链接 attention1attention2 引言 本文主要详解 transformer 的算法结构以及理论解释&#xff0c;代码实现以及具体实现时候的细节放在下一篇来详述。 下面就通过上图中 transformer 的结构来依次解析 输入部分(Encode 侧) input 输出主要包含 两个部分&#xff1a…

[acwing周赛复盘] 第 120 场周赛20230909

[acwing周赛复盘] 第 120 场周赛20230909 总结5146. 最大GCD1. 题目描述2. 思路分析3. 代码实现 5147. 数量1. 题目描述2. 思路分析3. 代码实现 5148. 字符串匹配1. 题目描述2. 思路分析3. 代码实现 六、参考链接 总结 T1好难啊。T1 数学T2 dfs/数位dpT3 计数贪心 5146. 最大…