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

news2024/11/25 20:38:50

        本篇博客详细介绍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*  Ps
 

int 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 7

int 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.coloradmin.cn/o/1400185.html

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

相关文章

Java 数组原理内存图

Java 虚拟机的内存划分 为了提高运算效率&#xff0c;就对空间进行了不同区域的划分&#xff0c;因为每一片区域都有特定的处理数据方式和内存管理方式。 JVM 的内存划分 数组在内存中的存储 一个数组内存图 public static void main(String[] args) {int[] nums new int[] {1…

64位ATT汇编语言as汇编ld链接,执行报错Segmentation fault

absCallAndPrintAbsAsLd.s里边的内容如下&#xff1a; .section .datastringToShow:.ascii "The abs of number is %d\n\0" .global _start .section .text _start:pushq %rbpmovq %rsp,%rbpmovq $-5,%rdicall absmovq $stringToShow,%rdimovq %rax,%rsicall printf…

鸿蒙星河版启航,开发者驶入生态新征程

操作系统市场的气候已经不同以往。在鸿蒙决定不再兼容安卓之后&#xff0c;这里正欲长出一片全新的天地。 四年前&#xff0c;华为鸿蒙系统横空出世&#xff0c;彼时它还不完全与安卓和iOS的性质划等号&#xff0c;而是定义为物联网操作系统。而如今的华为鸿蒙要改写故事篇章&…

关于C语言整型提升的讲解

目录 1.什么是整型提升 2.整型提升的意义 3.整型提升是怎么提升的 4.整型提升的实例 1.什么是整型提升 C语言中的整型算术运算总是以缺省&#xff08;默认&#xff09;整型类型的精度来进行的。为了获得这个精度&#xff0c;表达式中的字符和短整型操作数在使用之前会被转换…

go语言(十一)----面向对象继承

一、面向对象继承 写一个父类 package mainimport "fmt"type Human struct {name stringsex string }func (this *Human) Eat() {fmt.Println("Human.Eat()...") }func (this *Human) Walk() {fmt.Println("Human.Walk()...") }func main() {h…

JNPF低代码开发平台总体架构介绍

目录 一、JNPF介绍 二、团队能力 三、技术选型 1.后端技术栈 2.前端技术栈 3.数据库支持 四、JNPF界面示意图 五、开发环境 一、JNPF介绍 JNPF是一款企业级低代码开发平台。基于Springboot、Vue技术&#xff0c;采用微服务、前后端分离架构&#xff0c;基于可视化数据建…

虹科分享 | Redis与MySQL协同升级企业缓存

文章速览&#xff1a; MySQL为什么需要Redis EnterpriseRedis Enterprise带来哪些优势Redis Enterprise与MySQL协同 传统的MySQL数据库在处理大规模应用时已经到了瓶颈&#xff0c;Redis Enterprise怎样助力突破这一瓶颈&#xff1f;Redis Enterprise与MYSQL共同用作企业级缓存…

ChatGPT 如何解决 “Something went wrong. lf this issue persists ….” 错误

Something went wrong. If this issue persists please contact us through our help center at help.openai.com. ChatGPT经常用着用着就出现 “Something went wrong” 错误&#xff0c;不管是普通账号还是Plus账号&#xff0c;不管是切换到哪个节点&#xff0c;没聊两次就报…

什么是中间件?

文章目录 为什么需要中间件&#xff1f;中间件生态漫谈数据库中间件读写分离分库分表引进数据库中间件MyCat 服务端代理模式ShardingJDBC 客户端代理模式 总结 IT 系统从单体应用逐渐向分布式架构演变&#xff0c;高并发、高可用、高性能、分布式等话题变得异常火热&#xff0c…

开源项目_一键发布 markdown 到各个平台_Wechatsync

1 使用场景 最近文章更新比较频繁&#xff0c;基本是日更。因此花费了相当多的时间在不同平台之间同步。时间主要消耗在&#xff1a;需要大致浏览一遍文章内容&#xff0c;另外某些平台需要手动上传图片&#xff0c;有时还需要调整排版。 为了解决这个问题&#xff0c;我选择…

Addressables(1) 从安装到加载单个/多个资源

不想再配改那些狗屎路径&#xff0c;准备研究一下Adressable&#xff0c;据说可以用key加载指定的资源 刚安装下来&#xff0c;随便搞了个资源勾选了一下addressable的框框&#xff0c;多了好多东西啊 概念铺天盖地而来&#xff0c;ok 没事的 慢慢来&#xff01; 前置知识 P…

【llm 使用llama 小案例】

huggingfacehttps://huggingface.co/meta-llama from transformers import AutoTokenizer, LlamaForCausalLMPATH_TO_CONVERTED_WEIGHTS PATH_TO_CONVERTED_TOKENIZER # 一般和模型地址一样model LlamaForCausalLM.from_pretrained(PATH_TO_CONVERTED_WEIGHTS) tokenize…

短视频解析单页源码

这个一个网页html解析短视频无水印视频的源码&#xff0c;电脑直接打开也可以本地使用&#xff0c;也可以上传到网站搭建成网页使用。 代码如下&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>短视频解析&l…

计算机毕业设计选题分享-ssm租房小程序42196(赠送源码数据库)JAVA、PHP,node.js,C++、python,大屏数据可视化等

毕业设计 ssm租房小程序 院 系&#xff1a; 姓 名&#xff1a; 学 号&#xff1a; 专 业&#xff1a; 年 级&#xff1a; 指导教师&#xff1a; 职 称&#xff1…

HYBBS 表白墙网站PHP程序源码 可封装成APP

源码介绍 PHP表白墙网站源码&#xff0c;可以做校园内的&#xff0c;也可以做校区间的&#xff0c;可封装成APP。告别QQ空间的表白墙吧。 安装PHP5.6以上随意 上传程序安装&#xff0c;然后设置账号密码&#xff0c;登陆后台切换模板手机PC都要换开启插件访问前台。 安装完…

MySQL隔离性的进阶理解

数据库的并发场景有三种 读-读&#xff1a;没有问题读-写&#xff1a;有线程安全问题&#xff0c;可能会造成事务隔离性问题&#xff0c;如&#xff1a;脏读&#xff0c;幻读&#xff0c;不可重复读写-写&#xff1a;有线程安全问题&#xff0c;可能会存在更新丢失问题&#xf…

使用OpenCV绘制图形

使用OpenCV绘制图形 绘制黄色的线&#xff1a; # 绘制一个黑色的背景画布 canvas np.zeros((300, 300, 3), np.uint8) # 在画布上&#xff0c;绘制一条起点坐标为(150, 50)、终点坐标为(150, 250)&#xff0c;黄色的&#xff0c;线条宽度为20的线段 canvas cv2.line(canvas,…

AI基于近邻图的向量搜索案例(二)

Graph部分 Graph部分&#xff0c;通过先构建KNN图&#xff0c;再根据RNG Rule移除不符合要求的边&#xff0c;得到RNG。 KNN图的构建 KNN图是指对于样本数据中的每一个点&#xff0c;将其自身与K个近邻点连接而形成的图。 由于样本数据规模非常大&#xff0c;我们采用了一定…

AI+量化02_金融市场的基础概念

文章目录 问答之纯小白 vs GPT4Q1. 请用尽可能简短的语句或例子&#xff0c;给小白讲解宏观经济Q2. 给小白讲解资本边际效率 思维导图 目标: 掌握量化金融知识、使用Python进行量化开发 背景&#xff1a;纯小白 参考资料&#xff1a;https://github.com/datawhalechina/whale-q…

Haxe-UnrealEngine5

Haxe-UnrealEngine5 结论 UE C header > External/**.hx.hx > .h/.cpp&#xff0c;和 UE C 一起编译使用 hxcpp 来调试 .hx good&#xff1a; 理论上不仅限反射代码走 UE C&#xff0c;无需维护 backend&#xff0c;比如 Lua Binding理论上接近 UE C 的性能 bad&…