12.C语言中的struct详解:定义、赋值、指针、嵌套与位字段
目录
- 1.简介
- 2.struct 的复制
- 3.struct 指针
- 4.struct 的嵌套
- 5.位字段
- 6.弹性数组成员
1.简介
本篇原文为:C语言中的struct详解:定义、赋值、指针、嵌套与位字段。
更多C++进阶、rust、python、逆向等等教程,可点击此链接查看:酷程网
C 语言内置的数据类型,除了最基本的几种原始类型,只有数组属于复合类型,可以同时包含多个值,但是只能包含相同类型的数据,实际使用中并不够用。
实际使用中,主要有下面两种情况,需要更灵活强大的复合类型。
- 复杂的物体需要使用多个变量描述,这些变量都是相关的,最好有某种机制将它们联系起来。
- 某些函数需要传入多个参数,如果一个个按照顺序传入,非常麻烦,最好能组合成一个复合结构传入。
为了解决这些问题,C 语言提供了struct
关键字,允许自定义复合数据类型,将不同类型的值组合在一起,这样不仅为编程提供方便,也有利于增强代码的可读性。
C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。
下面是struct
自定义数据类型的一个例子:
struct fraction {int numerator;int denominator;
};
上面示例定义了一个分数的数据类型struct fraction
,包含两个属性numerator
和denominator
。
注意,作为一个自定义的数据类型,它的类型名要包括struct
关键字,比如上例是struct fraction
,单独的fraction
没有任何意义,甚至脚本还可以另外定义名为fraction
的变量,虽然这样很容易造成混淆。
另外,struct
语句结尾的分号不能省略,否则很容易产生错误。
定义了新的数据类型以后,就可以声明该类型的变量,这与声明其他类型变量的写法是一样的:
struct fraction f1;f1.numerator = 22;
f1.denominator = 7;
上面示例中,先声明了一个struct fraction
类型的变量f1
,这时编译器就会为f1
分配内存,接着就可以为f1
的不同属性赋值。
可以看到,struct 结构的属性通过点(.
)来表示,比如numerator
属性要写成f1.numerator
。
再提醒一下,声明自定义类型的变量时,类型名前面,不要忘记加上struct
关键字。也就是说,必须使用struct fraction f1
声明变量,不能写成fraction f1
。
除了逐一对属性赋值,也可以使用大括号,一次性对 struct 结构的所有属性赋值:
struct car {char* name;float price;int speed;
};struct car saturn = {"Saturn SL/2", 16000.99, 175};
上面示例中,变量saturn
是struct car
类型,大括号里面同时对它的三个属性赋值,如果大括号里面的值的数量,少于属性的数量,那么缺失的属性自动初始化为0
。
注意,大括号里面的值的顺序,必须与 struct 类型声明时属性的顺序一致,否则,必须为每个值指定属性名:
struct car saturn = {.speed=172, .name="Saturn SL/2"};
上面示例中,初始化的属性少于声明时的属性,这时剩下的那些属性都会初始化为0
。
注意{}中字段名前面有一个点.
,不要写会报错。
声明变量以后,可以修改某个属性的值:
struct car saturn = {.speed=172, .name="Saturn SL/2"};
saturn.speed = 168;
上面示例将speed
属性的值改成168
。
struct 的数据类型声明语句与变量的声明语句,可以合并为一个语句。
struct book {char title[500];char author[100];float value;
} b1;
上面的语句同时声明了数据类型book
和该类型的变量b1
。如果类型标识符book
只用在这一个地方,后面不再用到,这里可以将类型名省略。
struct {char title[500];char author[100];float value;
} b1;
上面示例中,struct
声明了一个匿名数据类型,然后又声明了这个类型的变量b1
。
与其他变量声明语句一样,可以在声明变量的同时,对变量赋值:
struct {char title[500];char author[100];float value;
} b1 = {"Harry Potter", "J. K. Rowling", 10.0},b2 = {"Cancer Ward", "Aleksandr Solzhenitsyn", 7.85};
上面示例中,在声明变量b1
和b2
的同时,为它们赋值。
下一章介绍的typedef
命令可以为 struct 结构指定一个别名,这样使用起来更简洁:
typedef struct cell_phone {int cell_no;float minutes_of_charge;
} phone;phone p = {5551234, 5};
上面示例中,phone
就是struct cell_phone
的别名。
指针变量也可以指向struct
结构:
struct book {char title[500];char author[100];float value;
}* b1;// 或者写成两个语句
struct book {char title[500];char author[100];float value;
};
struct book* b1;
上面示例中,变量b1
是一个指针,指向的数据是struct book
类型的实例。
struct 结构也可以作为数组成员:
struct fraction numbers[1000];numbers[0].numerator = 22;
numbers[0].denominator = 7;
上面示例声明了一个有1000个成员的数组numbers
,每个成员都是自定义类型fraction
的实例。
struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用属性的存储空间的倍数,其他属性会添加空位与之对齐,这样可以提高读写效率:
struct foo {int a;char* b;char c;
};
printf("%d\n", sizeof(struct foo)); // 24
上面示例中,struct foo
有三个属性,在64位计算机上占用的存储空间分别是:int a
占4个字节,指针char* b
占8个字节,char c
占1个字节。它们加起来,一共是13个字节(4 + 8 + 1)。
但是实际上,struct foo
会占用24个字节,原因是它最大的内存占用属性是char* b
的8个字节,导致其他属性的存储空间也是8个字节,这样才可以对齐,导致整个struct foo
就是24个字节(8 * 3)。
多出来的存储空间,都采用空位填充,所以上面的struct foo
真实的结构其实是下面这样:
struct foo {int a; // 4char pad1[4]; // 填充4字节char *b; // 8char c; // 1char pad2[7]; // 填充7字节
};
printf("%d\n", sizeof(struct foo)); // 24
为什么浪费这么多空间进行内存对齐呢?
这是为了加快读写速度,把内存占用划分成等长的区块,就可以快速在 Struct 结构体中定位到每个属性的起始地址。
由于这个特性,在有必要的情况下,定义 Struct 结构体时,可以采用存储空间递增的顺序,定义每个属性,这样就能节省一些空间。
struct foo {char c;int a;char* b;
};
printf("%d\n", sizeof(struct foo)); // 16
上面示例中,占用空间最小的char c
排在第一位,其次是int a
,占用空间最大的char* b
排在最后。整个strct foo
的内存占用就从24字节下降到16字节。
2.struct 的复制
struct 变量可以使用赋值运算符(=
),复制给另一个变量,这时会生成一个全新的副本。
系统会分配一块新的内存空间,大小与原来的变量相同,把每个属性都复制过去,即原样生成了一份数据。这一点跟数组的复制不一样,务必小心:
struct cat { char name[30]; short age; } a, b;strcpy(a.name, "Hula");
a.age = 3;b = a;
b.name[0] = 'M';printf("%s\n", a.name); // Hula
printf("%s\n", b.name); // Mula
上面示例中,变量b
是变量a
的副本,两个变量的值是各自独立的,修改掉b.name
不影响a.name
。
上面这个示例是有前提的,就是 struct 结构的属性必须定义成字符数组,才能复制数据,如果稍作修改,属性定义成字符指针,结果就不一样:
struct cat { char* name; short age; } a, b;a.name = "Hula";
a.age = 3;b = a;
上面示例中,name
属性变成了一个字符指针,这时a
赋值给b
,导致b.name
也是同样的字符指针,指向同一个地址,也就是说两个属性共享同一个地址。
因为这时,struct 结构内部保存的是一个指针,而不是上一个例子的数组,这时复制的就不是字符串本身,而是它的指针。并且,这个时候也没法修改字符串,因为字符指针指向的字符串是不能修改的。
总结一下,赋值运算符(=
)可以将 struct 结构每个属性的值,一模一样复制一份,拷贝给另一个 struct 变量。这一点跟数组完全不同,使用赋值运算符复制数组,不会复制数据,只会共享地址。
注意,这种赋值要求两个变量是同一个类型,不同类型的 struct 变量无法互相赋值。
另外,C 语言没有提供比较两个自定义数据结构是否相等的方法,无法用比较运算符(比如==
和!=
)比较两个数据结构是否相等或不等。
3.struct 指针
如果将 struct 变量传入函数,函数内部得到的是一个原始值的副本。
#include <stdio.h>struct turtle {char* name;char* species;int age;
};void happy(struct turtle t) {t.age = t.age + 1;
}int main() {struct turtle myTurtle = {"MyTurtle", "sea turtle", 99};happy(myTurtle);printf("Age is %i\n", myTurtle.age); // 输出 99return 0;
}
上面示例中,函数happy()
传入的是一个 struct 变量myTurtle
,函数内部有一个自增操作。
但是,执行完happy()
以后,函数外部的age
属性值根本没变。原因就是函数内部得到的是 struct 变量的副本,改变副本影响不到函数外部的原始数据。
通常情况下,开发者希望传入函数的是同一份数据,函数内部修改数据以后,会反映在函数外部。而且,传入的是同一份数据,也有利于提高程序性能。
这时就需要将 struct 变量的指针传入函数,通过指针来修改 struct 属性,就可以影响到函数外部。
struct 指针传入函数的写法如下:
void happy(struct turtle* t) {
}happy(&myTurtle);
上面代码中,t
是 struct 结构的指针,调用函数时传入的是指针。struct 类型跟数组不一样,类型标识符本身并不是指针,所以传入时,指针必须写成&myTurtle
。
函数内部也必须使用(*t).age
的写法,从指针拿到 struct 结构本身:
void happy(struct turtle* t) {(*t).age = (*t).age + 1;
}
上面示例中,(*t).age
不能写成*t.age
,因为点运算符.
的优先级高于*
。*t.age
这种写法会将t.age
看成一个指针,然后取它对应的值,会出现无法预料的结果。
现在,重新编译执行上面的整个示例,happy()
内部对 struct 结构的操作,就会反映到函数外部。
(*t).age
这样的写法很麻烦。C 语言就引入了一个新的箭头运算符(->
),可以从 struct 指针上直接获取属性,大大增强了代码的可读性:
void happy(struct turtle* t) {t->age = t->age + 1;
}
总结一下,对于 struct 变量名,使用点运算符(.
)获取属性;对于 struct 变量指针,使用箭头运算符(->
)获取属性。
以变量myStruct
为例,假设ptr
是它的指针,那么下面三种写法是同一回事:
// ptr == &myStruct
myStruct.prop == (*ptr).prop == ptr->prop
4.struct 的嵌套
struct 结构的成员可以是另一个 struct 结构:
struct species {char* name;int kinds;
};struct fish {char* name;int age;struct species breed;
};
上面示例中,fish
的属性breed
是另一个 struct 结构species
。
赋值的时候有多种写法:
// 写法一
struct fish shark = {"shark", 9, {"Selachimorpha", 500}};// 写法二
struct species myBreed = {"Selachimorpha", 500};
struct fish shark = {"shark", 9, myBreed};// 写法三
struct fish shark = {.name="shark",.age=9,.breed={"Selachimorpha", 500}
};// 写法四
struct fish shark = {.name="shark",.age=9,.breed.name="Selachimorpha",.breed.kinds=500
};printf("Shark's species is %s", shark.breed.name);
上面示例展示了嵌套 Struct 结构的四种赋值写法。另外,引用breed
属性的内部属性,要使用两次点运算符(shark.breed.name
)。
下面是另一个嵌套 struct 的例子:
struct name {char first[50];char last[50];
};struct student {struct name name;short age;char sex;
} student1;strcpy(student1.name.first, "Harry");
strcpy(student1.name.last, "Potter");// or
struct name myname = {"Harry", "Potter"};
student1.name = myname;
上面示例中,自定义类型student
的name
属性是另一个自定义类型,如果要引用后者的属性,就必须使用两个.
运算符,比如student1.name.first
。
另外,对字符数组属性赋值,要使用strcpy()
函数,不能直接赋值,因为直接改掉字符数组名的地址会报错。
struct 结构内部不仅可以引用其他结构,还可以自我引用,即结构内部引用当前结构,比如,链表结构的节点就可以写成下面这样:
struct node {int data;struct node* next;
};
上面示例中,node
结构的next
属性,就是指向另一个node
实例的指针,下面,使用这个结构自定义一个数据链表:
struct node {int data;struct node* next;
};struct node* head;// 生成一个三个节点的列表 (11)->(22)->(33)
head = malloc(sizeof(struct node));head->data = 11;
head->next = malloc(sizeof(struct node));head->next->data = 22;
head->next->next = malloc(sizeof(struct node));head->next->next->data = 33;
head->next->next->next = NULL;// 遍历这个列表
for (struct node *cur = head; cur != NULL; cur = cur->next) {printf("%d\n", cur->data);
}
上面示例是链表结构的最简单实现,通过for
循环可以对其进行遍历。
5.位字段
struct 还可以用来定义二进制位组成的数据结构,称为“位字段”(bit field),这对于操作底层的二进制数据非常有用:
struct {unsigned int ab:1;unsigned int cd:1;unsigned int ef:1;unsigned int gh:1;
} synth;synth.ab = 0;
synth.cd = 1;
上面示例中,每个属性后面的:1
,表示指定这些属性只占用一个二进制位,所以这个数据结构一共是4个二进制位。
注意,定义二进制位时,结构内部的各个属性只能是整数类型。
实际存储的时候,C 语言会按照int
类型占用的字节数,存储一个位字段结构。如果有剩余的二进制位,可以使用未命名属性,填满那些位。
也可以使用宽度为0的属性,表示占满当前字节剩余的二进制位,迫使下一个属性存储在下一个字节:
struct {unsigned int field1 : 1;unsigned int : 2;unsigned int field2 : 1;unsigned int : 0;unsigned int field3 : 1;
} stuff;
上面示例中,stuff.field1
与stuff.field2
之间,有一个宽度为两个二进制位的未命名属性。stuff.field3
将存储在下一个字节。
6.弹性数组成员
很多时候,不能事先确定数组到底有多少个成员,如果声明数组的时候,事先给出一个很大的成员数,就会很浪费空间。
C 语言提供了一个解决方法,叫做弹性数组成员(flexible array member)。
如果不能事先确定数组成员的数量时,可以定义一个 struct 结构:
struct vstring {int len;char chars[];
};
上面示例中,struct vstring
结构有两个属性。len
属性用来记录数组chars
的长度,chars
属性是一个数组,但是没有给出成员数量。
chars
数组到底有多少个成员,可以在为vstring
分配内存时确定:
struct vstring* str = malloc(sizeof(struct vstring) + n * sizeof(char));
str->len = n;
上面示例中,假定chars
数组的成员数量是n
,只有在运行时才能知道n
到底是多少。
然后,就为struct vstring
分配它需要的内存:它本身占用的内存长度,再加上n
个数组成员占用的内存长度。最后,len
属性记录一下n
是多少。
这样就可以让数组chars
有n
个成员,不用事先确定,可以跟运行时的需要保持一致。
弹性数组成员有一些专门的规则。首先,弹性成员的数组,必须是 struct 结构的最后一个属性。另外,除了弹性数组成员,struct 结构必须至少还有一个其他属性。
相关文章:
12.C语言中的struct详解:定义、赋值、指针、嵌套与位字段
目录 1.简介2.struct 的复制3.struct 指针4.struct 的嵌套5.位字段6.弹性数组成员 1.简介 本篇原文为:C语言中的struct详解:定义、赋值、指针、嵌套与位字段。 更多C进阶、rust、python、逆向等等教程,可点击此链接查看:酷程网 …...
文件读写到SQLite数据库的方法
在 SQLite 数据库中,将文件读写到数据库的常见方法主要有以下几种: 1. 将文件以 BLOB 类型存储 BLOB(Binary Large Object) 是 SQLite 中的二进制数据类型,可以直接用来存储文件内容。 步骤: 创建表 创建一…...

springboot项目部署至linux
1.修改pom.xml 确认是否有以下代码,没有请进行添加,mainClass改成你的启动类 <plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.ve…...

使用sed命令封装自定义dos2unix脚本
使用sed命令封装自定义dos2unix脚本 创建 `dos2unix` 脚本使用自定义的 `dos2unix` 脚本注意事项要将 sed -i 封装为一个简单的 dos2unix 脚本,你可以创建一个 Bash 脚本文件,该文件接受文件名作为参数,并使用 sed 命令来删除文件中的 DOS 回车符(\r)。以下是一个基本的实…...

调整Python+Pytest+Allure+Yaml+Pymysql框架中需要执行的用例顺序
当pytest框架中有时时候会因为用例的前后关联关系需要调整用例执行顺序时则可以跟进具体的要求调整pytest.ini配置文件中执行用例文件夹的前后顺序 当如果是需要调整某个文件夹中用例的执行顺序时,则跟进具体的文件调整对应testcases中test_*.py文件中的执行顺序...
带内管理和带外管理
带内管理(In-Band Management) 概述 带内管理是一种借助生产网络来传输管理数据的网络管理方式,其管理流量与业务流量共享相同的网络路径。 特点 共享网络路径:管理数据和业务数据一同使用现有的网络基础设施,在同…...
【操作系统】阻塞非阻塞I/O、同步异步I/O
阻塞I/O:程序发起I/O操作时,程序被挂起,直到I/O操作完成并返回结果。在这个过程中,程序会被阻塞无法执行其他任务。适用于简单、低并发的场景。 非阻塞I/O:程序发起I/O操作时,不会等待,立即返回…...
spring cloud alibaba-dubbo3 rpc运行原理
Dubbo3 运行原理 Dubbo3 是 Apache Dubbo 的最新版本,是一个高性能、轻量级的分布式服务框架,支持微服务架构。相比 Dubbo2,它在协议、扩展性、服务治理、流控等方面做了大量改进,特别是引入了 Triple 协议,使其更加适…...

【Uniapp-Vue3】computed计算属性用法及方法对比
如果我们想要将两个响应式变量进行某种运算,就可以使用computed计算属性。 比如下面这个例子中,输入名和姓合成全名,可以用直接显示的方法: 我们也可以使用computed属性: import {computed} from "vue"; le…...

web实操10——Filter和Listener
Filter介绍 web三大组件:servlet,filter, lisenter。 Filter快速入门 步骤 拦截路径:你访问什么样的资源,过滤器会生效,包括静态资源,动态资源。 配置:两种配置方式 代码实现 代码&#…...
Spring中,出现依赖不完全注入后才执行逻辑
1. Bean生命周期机制 Spring管理的Bean是通过生命周期回调进行初始化和依赖注入的。以下是典型的生命周期阶段: 实例化(Instantiation): 创建Bean对象。依赖注入(Dependency Injection): 向Be…...
如何选择 Dockerfile 的放置方式
是否将 Dockerfile 放在项目根目录下还是为每个应用服务单独创建 Dockerfile,取决于项目架构和使用场景。以下是针对不同项目类型的最佳实践和推荐方式: 一、单体应用项目 项目特点 项目是一个单体应用,只有一个运行环境,例如&a…...
用 HTML5 Canvas 和 JavaScript 实现炫酷跨年烟花特效
一、引言 跨年夜,五彩斑斓、绚丽绽放的烟花是最令人期待的视觉盛宴之一。在网页端,我们能否通过技术手段复现这一梦幻场景呢?答案是肯定的。本文将深入剖析一段使用 HTML5 Canvas 和 JavaScript 实现的跨年烟花特效源码,带你领略前端技术创造的惊艳画面。 用 HTML5 Canvas…...

cat命令详解
🏝️专栏:https://blog.csdn.net/2301_81831423/category_12872319.html 🌅主页:猫咪-9527-CSDN博客 “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” cat 是 Linux/Unix 中的一个非常常用的命令&…...

el-table 自定义表头颜色
第一种方法:计算属性 <template><div><el-table:data"formData.detail"border stripehighlight-current-row:cell-style"{ text-align: center }":header-cell-style"headerCellStyle"><el-table-column fixed…...
window.print()预览时表格显示不全
问题描述:使用element的table组件,表格列宽为自适应,但使用window.print()方法预览的页面会直接按预览宽度截取表格内容进行展示,造成表格可能的显示不全问题 解决方法:添加如下样式 media print {::v-deep {// 表头…...
React Router底层核心原理详解
React Router 是一个功能强大的路由库,它允许开发者在 React 单页面应用(SPA)中实现客户端路由管理。React Router 通过匹配 URL 和组件的关系来实现页面的导航,它不仅提供了简单的 API,还在底层实现了复杂的 URL 匹配…...
linux MySQL 实时性能监控工具
在 Linux 上,有多个工具可以用于 实时监控 MySQL 的性能。根据你的需求,以下是常用的 MySQL 实时性能监控工具,包括轻量级的命令行工具和可视化监控工具。 🔧 1. MySQLTuner – 一键性能优化建议 ✅ 特点 快速分析 MySQL 的性能…...

ModuleNotFoundError: No module named ‘setuptools_rust‘ 解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

基于Spring Boot的海滨体育馆管理系统的设计与实现
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的海滨体育馆管理系统的设计与实现。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 宠物医院…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...