C语言总结十一:自定义类型:结构体、枚举、联合(共用体)

        本篇博客详细介绍C语言最后的三种自定义类型,它们分别有着各自的特点和应用场景,重点在于理解这三种自定义类型的声明方式和使用,以及各自的特点,最后重点掌握该章节常考的考点,如:结构体内存对齐问题,使用联合判断字节序的存储问题。学完本篇博客达到理解会运用!

一、结构体(struct)

      数组可理解为一种自定义数据类型,它存放的是一组相同数据类型数据的集合,每个数据可看作是一个变量,但是现实生活只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。描述一个学生需要名字、年龄、学号、身高、体重等;描述一本书需要作者、出版社、定价等。需要不同的数据类型,因此,C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。

      结构体是一种存放不同数据类型的数据集合,同样也是一种自定义数据类型。结构体是C语言由面向过程向面向对象的一种转变。

1.1 结构体类型的声明和结构体的嵌套以及结构体指针(是指针)

       结构体类型的声明通常包括结构体的名称和其成员变量的定义,结构体的成员变量可以包括基本数据类型变量、数组、结构体类型(结构体的嵌套)指针类型

      结构体指针是指向结构体类型变量内存地址的指针。通过使用结构体指针可以访问或者修改结构体成员变量;

      下面使用一个具体的例子说明如何进行结构体类型的声明和结构体的嵌套以及定义结构体指针。

       结构体的声明必须放在函数的外部,它告知编译器这个结构体包括哪些成员,按照如下格式进行声明:

              struct  结构体名
             {
                        成员列表(结构体所包含的变量或数组或结构体或指针)
             }
;

     此时,结构体名就是类型名。

//.c文件中(C语言语法)普通的声明结构体的方式和定义结构体变量
struct Address
{char name[20];char area[20];
};                  //分号不能少struct Student
{char * name1;         //字符串指针const char * name2;   //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改char name3[20];       //字符数组int id;float  score;Address address;     //结构体嵌套
};         int main()
{//定义结构体变量struct Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};//定义结构体指针struct Student * ps=&s;//.cpp文件中(C++语法)语普通的声明结构体的方式和定义结构体变量
struct Address
{char name[20];char area[20];
};                  //分号不能少struct Student
{char * name1;         //字符串指针const char * name2;   //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改char name3[20];       //字符数组int id;float  score;Address address;     //结构体嵌套
};         int main()
{//定义结构体变量Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};//定义结构体指针Student * ps=&s;return 0;
}

从上面可以看出,.c和.cpp语法的区别在于:c++语法可以省略关键字struct,而c语言语法不能省略struct,为了实际开发统一使用,可以结合使用typedef进行类型重命名,便可在两个文件同时使用,省略关键字struct。

//结构体声明结合typedef使用typedef struct Address
{char name[20];char area[20];
}Address;                  //分号不能少typedef struct Student
{char * name1;         //字符串指针const char * name2;   //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改char name3[20];       //字符数组int id;float  score;Address address;     //结构体嵌套
}Student,*Ps;         //利用typedef对结构体和结构体指针进行重命名
//相当于:typedef struct Student  Student
//相当于:typedef struct Student*  Psint main()
{//定义结构体变量Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};//定义结构体指针Ps=&s;

注意事项:

        在对结构体成员设计时,要考虑两个方面:

  1. 考虑数据的长度,用合适的数据类型变量保存!
  2. 对于较大的数据,可以用字符串进行保存!

如:字符数组 char str[20];    内存开辟在栈区,保存的是实际的数据
       字符串指针 const char * str;     字符串存储在常量区,指针开辟在栈区,保存的是这个数据的地址
        整型数据int存放不下时,可以用字符串的方式存储也可使用上述方法。

1.2 结构体变量的定义及初始化

      结构体和数组类似,因此初始化方式和数组一样,采用花括号赋值!结构体嵌套时,里面的结构体也是需要花括号进行初始化的。

       注意对结构体中的字符数组或者字符串进行初始化,必须使用字符串拷贝函数,切不可直接赋值,如:s.name="张三",因为左边是字符数组名为字符首元素的地址,地址是编号,是一个常量,常量不可以做左值,左值必须是可以修改的变量,右边是一个字符串常量存储在数据区,且不可以进行修改,代表首元素的地址。改正:strcpy(s.name,"张三");

注意事项:

      数组与结构体类似,但是存在以下两点不同之处:

  1. 结构体变量名和数组名不同,数组名在表达式中会被转换成指针/地址,而结构体变量名并不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址必须要加取地址符&;
  2. 数组在内存中存放的,并且随着下标的增大,数组元素的地址也随着增大,数组所占的字节数等于数组元素个数乘以单个元素所占字节数,而结构体所占内存空间的大小,需要结合内存对齐问题进行计算!
    Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};

       结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。 

1.3 结构体成员的三种访问方式

结构体成员的访问,一共有3种方式,但实际为了开发方便,常常使用2种

1.通过结构体变量访问结构体成员,语法形式:结构体变量.结构体成员名

2.通过结构体指针变量先解引用找到该结构体变量再 . 访问,

                                                         语法形式:(*结构体指针变量).结构体成员名

3.为了简化第二种引入指向符: -> ,语法形式:结构体指针变量->结构体成员名

        实际开发,常常用的是第1种和第3种,第3种更加实用。

#include <stdio.h>
typedef struct Address
{char name[20];char area[20];
}Address,* Pa;typedef struct Student
{const char* name1;   //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改int id;float  score;Address address;     //结构体嵌套
}Student, * Ps;int main()
{Student s = { "张三",22203,99.5,{"王恒","西安市"} };Ps p = &s;//使用代码实现访问s中的成员address中的area成员(嵌套访问)//第一种访问方式:通过结构体变量访问结构体成员:结构体变量.结构体成员名printf("%s ""%d ""%f ""%s ""%s \n",s.name1,s.id,s.score,s.address.name,s.address.area);//第二种访问方式:通过结构体指针变量先解引用找到该结构体变量再 . 访问printf("%s ""%d ""%f ""%s ""%s \n", (*p).name1, (*p).id, (*p).score, (*p).address.name, (*p).address.area);//第三种访问方式:为了简化第二种引入指向符: ->访问printf("%s ""%d ""%f ""%s ""%s ", p->name1, p->id, p->score, p->address.name, p->address.area);return 0;
}

1.4 结构体数组(是数组)

        结构体数组是包含结构体类型元素的数组。通过使用结构体数组,可以创建多个结构体类型的实例,并以数组的形式组织这些实例。

#include <stdio.h>// 结构体声明
typedef struct Person 
{char name[50];int age;float height;
}Person;int main() 
{// 创建结构体数组并初始化Person people[3] = {{"Alice", 30, 160.5},{"Bob", 25, 175.0},{"Charlie", 35, 180.2}};// 访问结构体数组元素for (int i = 0; i < 3; ++i) {printf("Person %d\n", i + 1);printf("Name: %s\n", people[i].name);printf("Age: %d\n", people[i].age);printf("Height: %.2f\n", people[i].height);printf("\n");}return 0;
}

1.5 结构体的内存对齐及结构体占用内存的计算(重点)

1.5.1 为什么存在内存对齐?

        计算机内存是以字节为单位划分的,理论上CPU可以访问任意编号的字节,但实际情况并非如此,CPU通过地址总线来访问内存,一次能处理几个字节的数据,就命令地址总线读取几个字节的数据,32位CPU一次可以处理四个字节的数据,那么就从内存中读取4个字节的数据少了浪费主频,多了没有用,64位处理器同样,每次读取8个字节。

       以32位CPU为例,实际寻址步长为4个字节,也就是只对地址编号为4的倍数的内存寻址,例如:4,8,12....这样做可以以最快的速度寻址,不遗漏一个字节,也不重复对一个字节寻址,对于程序来说,一个变量的最好位于一个寻址步长的范围内,这样一次就可以读取变量的值,如果跨步长存储,就需要读取两次,然后拼接数据,效率显然降低!

       将一个数据尽量放在一个步长之内,避免跨步长存储,这就要求各种数据类型按照一定的规则在内存空间上排列,这就是内存对齐,提高了读取效率,代价就是浪费内存空间!

     总体来说: 结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到???

让占用空间小的成员尽量集中在一起!!!

1.5.2 结构体大小的计算(重点)

1)结构体变量的首地址从偶数地址开始(用0表示)

(2)结构体每个成员相对于结构体首地址的偏移量都是min{该成员数据类型字节数,指定对齐方式}大小的整数倍,即每个成员在内存上开辟的起始地址必须是这个数,否则存在空间浪费。

(3)结构体的大小为min{结构体最大基本数据类型成员所占字节数(将嵌套结构体里的基本数据类型也算上,得到最大的基本数据类型所占字节数),指定对齐方式}大小的整数倍。

      预处理指令#pragma pack(n) 可以改变默认对齐数。n 取值是 1, 2, 4, 8 , 16。

vs中默认值 = 8,gcc中默认值 = 4

     
//练习1
#include<stdio.h>
struct S1
{char c1;int i;char c2;
};int main()
{printf("%d\n", sizeof(struct S1));return 0;
}

//练习2
#include<stdio.h>
struct S2
{char c1;char c2;int i;
};int main()
{printf("%d\n", sizeof(struct S2));return 0;
}

//练习3
#include<stdio.h>
struct S3
{double d;char c;int i;
};
int main()
{printf("%d\n", sizeof(struct S3));return 0;
}

//练习4-结构体嵌套问题
#include<stdio.h>struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf("%d\n", sizeof(struct S4));return 0;
}

 

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。 

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认#pragma pack(1)//设置默认对齐数为1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

1.6 结构体传参

       结构体变量名代表的是整个集合本身,作为函数参数时,传递的是整个集合,也就是说是原来结构体成员的一份临时拷贝,而不是像数组名一样被编译器转换成一个指针,如果结构体成员较多,尤其是成员为数组时,函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。传送的时间和空间开销会很大,影响程序的执行效率,此外,这种值传递不可以通过函数内部来修改外部数据!最好的办法是使用结构体指针,这时由实参传向形参只是传递一个地址,即结构体变量的地址,占用4个字节,非常迅速,并且可以在函数内部通过解引用结构体指针从而修改外部数据,另外如果不想改变外部的数据,可以给形参加上const进行修饰!

       结论: 结构体传参的时候,要传结构体的地址。

1.7 位段(了解)

1.7.1 什么是位段

      有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位段的数据结构。在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位段。

  1. 位段的成员必须是 int、unsigned int 或signed int 或者char。
  2. 位段的成员名后边有一个冒号和一个数字。
struct bs
{unsigned m;unsigned n: 4;unsigned char ch: 6;
};

:后面的数字用来限定成员变量占用的位数。成员 m 没有限制,根据数据类型即可推算出它占用 4 个字节(Byte)的内存。成员 n、ch 被:后面的数字限制,不能再根据数据类型计算长度,它们分别占用 4、6 位(Bit)的内存。C语言标准规定,位域的宽度不能超过它所依附的数据类型的长度。通俗地讲,成员变量都是有类型的,这个类型限制了成员变量的最大长度,:后面的数字不能超过这个长度。

1.7.2 位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

1.7.3 位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4.  当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。

总结:跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

二、枚举(enum)

2.1 枚举类型的定义

        在实际编程中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天,一年只有十二个月,一个班每周有六门课程等。#define命令虽然能解决问题,但也带来了不小的副作用,导致宏名过多,代码松散,看起来总有点不舒服。C语言提供了一种枚举(Enum)类型,能够列出所有可能的取值,并给它们取一个名字。

#include <stdio.h>#define Mon 1
#define Tues 2
#define Wed 3
#define Thurs 4
#define Fri 5
#define Sat 6
#define Sun 7int main(){int day;scanf("%d", &day);switch(day){case Mon: puts("Monday"); break;case Tues: puts("Tuesday"); break;case Wed: puts("Wednesday"); break;case Thurs: puts("Thursday"); break;case Fri: puts("Friday"); break;case Sat: puts("Saturday"); break;case Sun: puts("Sunday"); break;default: puts("Error!");}return 0;
}

枚举类型定义方式如下:

      enum 枚举类型名字 {枚举值列表};

      enum是一个新的关键字,专门用来定义枚举类型,这也是它在C语言中的唯一用途;注意最后的;不能少。并且枚举值必须是有符号整型类型。枚举类型变量需要存放的是一个整数,它的字节长度和 int 相同。

例如,列出一个星期有几天:
enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun };
可以看到,我们仅仅给出了名字,却没有给出名字对应的值,这是因为枚举值默认从 0 开始,往后逐个加 1(递增);也就是说,week 中的 Mon、Tues ...... Sun 对应的值分别为 0、1 ...... 6。我们也可以给每个名字都指定一个值:
enum week{ Mon = 1, Tues = 2, Wed = 3, Thurs = 4, Fri = 5, Sat = 6, Sun = 7 };更为简单的方法是只给第一个名字指定值:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
这样枚举值就从 1 开始递增,跟上面的写法是等效的。

枚举是一种类型,通过它可以定义枚举变量 ,有了枚举变量,就可以把列表中的值赋给它


enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
enum week a, b, c;
enum week a = Mon, b = Wed, c = Sat;
判断用户输入的是星期几。
#include <stdio.h>
int main(){enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day;scanf("%d", &day);switch(day){case Mon: puts("Monday"); break;case Tues: puts("Tuesday"); break;case Wed: puts("Wednesday"); break;case Thurs: puts("Thursday"); break;case Fri: puts("Friday"); break;case Sat: puts("Saturday"); break;case Sun: puts("Sunday"); break;default: puts("Error!");}return 0;
}

需要注意的两点是:
1) 枚举列表中的 Mon、Tues、Wed 这些标识符的作用范围是全局的(严格来说是 main() 函数内部),不能再定义与它们名字相同的变量。
2) Mon、Tues、Wed 等都是常量,不能对它们赋值,只能将它们的值赋给其他的变量。
        枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏。 

2.2 枚举类型的使用

enum Color//颜色
{RED=1,GREEN=2,BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;               //ok??错误!!!

2.3 枚举类型的特点

为什么使用枚举? 我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 便于调试
  4. 使用方便,一次可以定义多个常量

三、联合(共用体)(union)

       在C语言中,变量的定义是分配存储空间的过程。一般的,每个变量都具有其独有的 存储空间,那么可不可以在同一个内存空间中存储不同的数据类型(不是同时存储)呢? 答案是可以的,使用联合体就可以达到这样的目的。联合体也叫共用体,在C语言中 定义联合体的关键字是union。 

3.1 联合类型的定义

    它的定义格式为:

union 共用体名

{
        成员列表
};

    共用体也是一种自定义类型,可以通过它来创建变量

union data{int n;char ch;double f;
};
union data a, b, c;

3.2 联合类型的特点及利用联合判断判断字节序的存储方式

3.2.1 特点

       联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联 合至少得有能力保存最大的那个成员)。

#include<stdio.h>
union Un
{int i;char c;
};
union Un un;int main()
{// 下面输出的结果是一样的吗?printf("%d\n", &(un.i));printf("%d\n", &(un.c));
在联合体中,所有成员共享同一块内存,因此这两行输出的地址应该是相同的。//下面输出的结果是什么?un.i = 0x11223344;un.c = 0x55;printf("%x\n", un.i);;
//由于联合体的特性,此时整型成员i的值将被覆盖为字符型成员c的值,因此输出的结果将是55。return 0;
}

3.2.2 利用联合判断判断字节序的存储方式

#include <stdio.h>
union union_test 
{int s; char t;
}test;int main(){printf("联合体 union_test 所占的字节数:%d\n", sizeof(union union_test));test.s = 0x12345678;if (0x78 == test.t) {printf("小端模式\n");}else {printf("大端模式\n");}return 0;
}

       以上便是自定义类型全部内容,认真理解消化,一定会有极大的收获,可以留下你们点赞、关注、评论,您的支持是对我极大的鼓励,下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

batchsamper学习总结

欢迎关注我们组的微信公众号&#xff0c;更多好文章在等你呦&#xff01; 微信公众号名&#xff1a;碳硅数据 公众号二维码&#xff1a; 今天看代码看到了一个很好的关于batchsampler的实现&#xff0c;做了一些测试&#xff0c;记录一下 import torch from torch.utils.data …

如何才能拥有比特币 - 01 ?

如何才能拥有BTC 在拥有 BTC 之前我们要先搞明白 BTC到底保存在哪里&#xff1f;我的钱是存在银行卡里的&#xff0c;那我的BTC是存在哪里的呢&#xff1f; BTC到底在哪里&#xff1f; 一句话概括&#xff0c;BTC是存储在BTC地址中&#xff0c;而且地址是公开的&#xff0c;…

企业级大数据安全架构(五)Knox安装

本章节介绍安全架构里面一个重要组件Knox安装&#xff0c;我们是通过ambari安装&#xff0c;如果安装开源Knox可参考官网文档 1 添加服务 打开Ambari页面并登录&#xff0c;然后点击主页上的"Add Service"按钮。 2 Knox勾选 在服务列表中&#xff0c;找到并勾选…

​第14节-高质量简历写作求职通关-在线测试

在线测试主要包括性格测试、综合能力测试、技能测试三类 性格测试 性格测试主要用于考察个人与工岗位的匹配程度 考察内容包含性格、能力、动机、价值观等&#xff0c;考察形式一般为给出相应的工作场景&#xff0c;让你选择最喜欢或者最不喜欢的答案 技能考试 这类测试一般是针…

redis优化系列(六)

本期分享redis内存过期策略&#xff1a;过期key的处理 Redis之所以性能强&#xff0c;最主要的原因就是基于内存存储。然而单节点的Redis其内存大小不宜过大&#xff0c;会影响持久化或主从同步性能。 可以通过修改配置文件来设置Redis的最大内存&#xff1a; maxmemory 1gb …

Android14之DefaultKeyedVector实现(一百八十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【前沿技术杂谈:智能对话的未来】深入比较ChatGPT与文心一言

【前沿技术杂谈&#xff1a;智能对话的未来】深入比较ChatGPT与文心一言 引言主体智能回复语言准确性知识库丰富度 深入分析&#xff1a;ChatGPT与文心一言的技术对比技术架构和算法数据处理和隐私用户界面和体验 应用场景分析未来展望技术进步的趋势潜在的挑战对社会的影响 结…

Element组件完整引入、按需引入、样式修改(全局、局部)、简单安装less以及npm命令证书过期等

目录 一、npm 安装二、完整引入三、按需引入四、样式修改1.按需加载的全局样式修改2. 局部样式修改1. 在 css 预处理器如 less scss 等直接使用::v-deep2. 只能用在原生 CSS 语法中:/deep/ 或者 >>> 五、 拓展&#xff1a;npm 安装less报错&#xff0c;提示证书过期六…

爬虫requests+综合练习详解

Day2 - 1.requests第一血_哔哩哔哩_bilibili requests作用&#xff1a;模拟浏览器发请求 requests流程&#xff1a;指定url -> 发起请求 -> 获取响应数据 -> 持续化存储 爬取搜狗首页的页面数据 import requests# 指定url url https://sogou.com # 发起请求 resp…

Java-NIO篇章(4)——Selector选择器详解

Selector介绍 选择器&#xff08;Selector&#xff09;是什么呢&#xff1f;选择器和通道的关系又是什么&#xff1f;这里详细说明&#xff0c;假设不用选择器&#xff0c;那么一个客户端请求数据传输那就需要建立一个连接&#xff0c;为了避免线程阻塞&#xff0c;那么每个客…

中仕教育:国考调剂和补录的区别是什么?

国考笔试成绩和进面名单公布之后&#xff0c;考生们就需要关注调剂和补录了&#xff0c;针对二者之间的区别很多考生不太了解&#xff0c;本文为大家解答一下关于国考调剂和补录的区别。 1.补录 补录是在公式环节之后进行的&#xff0c;主要原因是经过面试、体检和考察&#…

了解森林消防灭火泵:为何它是森林安全的关键

在森林火灾中&#xff0c;火势蔓延速度极快&#xff0c;一旦发生火灾&#xff0c;很难及时控制和扑灭。传统的灭火方法主要是利用水扑救&#xff0c;这种方法具有经济、简单、有效等优点。然而&#xff0c;在我国森林火灾中&#xff0c;水资源一直没有得到充分的利用。至今&…

基于Django的计算机编程技术学习与服务平台

临近毕业&#xff0c;又到了赶毕设的时候了&#xff0c;本次介绍分享一下自己的毕业设计项目吧。 项目主题&#xff1a;基于Django的计算机技术编程技术学习与服务平台 实现功能&#xff1a; 1.登入&#xff1a;用户的登陆注册 2.Python教程&#xff1a;实现用户的Python技…

苹果笔记本 macbook 在 office word 中使用 mathtype 的方法

前言 想在 MacBook 中使用 mathtype&#xff0c;去搜索&#xff0c;去 Apple Store 下载也发现没有 解决方法 打开 office Word 的「插入」中的「获取加载项」、「我的加载项」。 在应用商店中下载&#xff0c;需要登录自己的微软账号。 加载成功后就可以使用了。 注意 和…

【IP-Adapter】进阶 - 同款人物【2】 ☑

测试模型&#xff1a;###最爱的模型\flat2DAnimerge_v30_2.safetensors [b2c93e7a89] 原图&#xff1a; 加入 control1 [IP-Adapter] 加入 control 2 [OpenPose] 通过openpose骨骼图修改人物动作。 加入 control 3 lineart 加入cotrol3 …

Labview for循环精讲

本文详细介绍Labview中For循环的使用方法&#xff0c;从所有细节让你透彻的看明白For循环是如何使用的&#xff0c;如果有帮助的话记得点赞加关注~ 1. For循环结构 从最简单的地方讲起&#xff0c;一个常用的for循环结构是由for循环结构框图、循环次数、循环计数(i)三部分组成…

04 单链表

目录 链表的概念和结构单链表OJ练习 1. 链表的概念和结构 1.1 链表的概念 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 1.从上图可以看出链式结构在逻辑上是连续的&#xff0c;物理上不一定连续 2.现…

html 会跳舞的时间动画特效

下面是是代码&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <meta h…

文件操作(上)

目录 文件的必要性&#xff1a; 文件分类&#xff1a; 程序文件&#xff1a; 数据文件&#xff1a; 文件的打开与关闭&#xff1a; fopen函数分析: ​编辑 FILE*: char*filename: char*mode: fclose函数&#xff1a; 应用&#xff1a; 文件编译 Fgetc Fputc 应用…

大模型微调实战笔记

大模型三要素 1.算法&#xff1a;模型结构&#xff0c;训练方法 2.数据&#xff1a;数据和模型效果之间的关系&#xff0c;token分词方法 3.算力&#xff1a;英伟达GPU&#xff0c;模型量化 基于大模型对话的系统架构 基于Lora的模型训练最好用&#xff0c;成本低好上手 提…